当前位置: 首页 > 后端技术 > Java

Proxy静态代理和动态代理

时间:2023-04-01 22:17:13 Java

设计模式简介意图:为其他对象提供代理,控制对整个对象的访问。主要解决:直接访问对象带来的问题,例如:要访问的对象在远程机器上。在面向对象的系统中,由于某些原因(比如对象创建成本很高,或者某些操作需要安全控制,或者需要进程外访问),直接访问会给用户或者系统结构带来很多麻烦,我们可以在访问这个对象的时候给这个对象加上一个访问层。优点:职责明确智能可扩展性强缺点:由于在客户端和真实主体之间增加了代理对象,某些类型的代理模型可能会减慢请求的处理速度。实施代理需要额外的工作,而且一些代理模式实施起来非常复杂。使用场景:远程代理虚拟代理Copy-on-Write代理保护代理缓存代理防火墙代理同步代理智能引用代理实现静态代理静态代理需要定义一个接口或者父类,代理对象和代理对象共同实现接口或继承相同的父类。接口interfaceMovable{voidmove();}是实现接口move方法的代理类publicclassTankimplementsMovable{/***模拟坦克移动一段时间*/@Overridepublicvoidmove(){System.出去。println("Tankmovingclaclacla...");尝试{Thread.sleep(newRandom().nextInt(10000));}catch(InterruptedExceptione){e.printStackTrace();代理类也实现了这个Interface,添加了Movable属性,实现了move方法内部调用传入的代理类的move方法,可以在调用前后添加一些业务代码。类TankTimeProxy实现Movable{Movablem;publicTankTimeProxy(Movablem){this.m=m;}@Overridepublicvoidmove(){longstart=System.currentTimeMillis();m.移动();长端=System.currentTimeMillis();System.out.println(结束-开始);}}最后在main方法中调用代理类的move方法。如果需要多个代理类,可以使用多层嵌套类Demo{publicstaticvoidmain(String[]args){Tankt=newTank();TankTimeProxyttp=newTankTimeProxy(t);//坦克日志代理tlp=newTankLogProxy(ttp);ttp.move();}}静态代理总结:可以在不修改目标对象的功能的情况下扩展目标功能缺点:因为代理对象需要和目标对象有相同的接口,所以会出现很多类型过多的代理类.同时,一旦方法被添加到接口中,目标对象和代理对象都必须维护。动态代理特点:代理对象不需要实现接口代理对象的生成。它们使用JDKAPI在内存中动态构建代理对象(cglibproxywithoutinterfaces)。代理类实现接口move方法publicclassTankimplementsMovable{/***模拟坦克移动一段时间*/@Overridepublicvoidmove(){System.out.println("Tankmovingclaclacla...");尝试{Thread.sleep(newRandom().nextInt(10000));}catch(InterruptedExceptione){e.printStackTrace();}}}JDK代理:代理类所在包:java.lang.reflect.ProxypublicstaticObjectnewProxyInstance(ClassLoaderloader,Class[]interfaces,InvocationHandlerh)ClassLoaderloader:指定当前要使用的目标对象类加载器Class[]interfaces:目标对象实现的接口类型InvocationHandlerh:事件处理,当目标对象的方法执行时,会触发事件处理器,并将其传递给方法当前执行的目标对象作为参数传入接口interfaceMovable{voidmove();}被代理,实现接口move方法publicclassTankimplementsMovable{/***模拟坦克移动一段时间*/@Overridepublicvoidmove(){System.out.println("Tankmovingclacla...");尝试{Thread.sleep(新随机().nextInt(10000));}catch(InterruptedExceptione){e.printStackTrace();}}}创建代理类调用处理程序classTimeProxyimplementsInvocationHandler{//代理类中的真实对象Movablem;publicTimeProxy(Movablem){this.m=m;}publicvoidbefore(){System.out.println("方法开始..");}publicvoidafter(){System.out.println("方法停止..");}@OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{before();对象o=method.invoke(m,args);后();返回o;}}Demo调用生成指定接口的代理类实例publicstaticvoidmain(String[]args){Tanktank=newTank();Movablem=(Movable)Proxy.newProxyInstance(Tank.class.getClassLoader(),newClass[]{Movable.class},//tank.class.getInterfaces()newTimeProxy(tank));m.move();}上面的演示生成了指定接口的代理类示例,但是在JDK中,通过JDKProxies或者cglib代理是在运行时自动生成的JDK在内存中动态代理生成动态代理文件的原理方法一:必须在main方法中执行,直接使用junit测试方法调用不能生成JDK1.8之后,在主要方法前面"jdk.proxy.ProxyGenerator.saveGeneratedFiles","true");JDK1.8和之前添加System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");在主要方法的前面;方法二:运行时添加jvm参数-Djdk.proxy.ProxyGenerator.saveGeneratedFiles=true。运行时添加jvm参数-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true。代理类的class文件会在当前目录的com包中生成。然后。..这是源代码过程。首先把上图截图,发现图片太多了。..Movablem=(Movable)Proxy.newProxyInstance(Tank.class.getClassLoader(),newClass[]{Movable.class},newTimeProxy(tank));/**查找或生成指定代理类及其构造函数。*/构造函数cons=getProxyConstructor(caller,loader,interfaces);returnproxyCache.sub(intf).computeIfAbsent(loader,(ld,clv)->newProxyBuilder(ld,clv.key()).build());Constructorbuild(){ClassproxyClass=defineProxyClass(module,interfaces);最终构造函数缺点;尝试{cons=proxyClass.getConstructor(constructorParams);}catch(NoSuchMethodExceptione){thrownewInternalError(e.toString(),e);}AccessController.doPrivileged(newPrivilegedAction(){publicVoidrun(){cons.setAccessible(true);returnnull;}});returncons;}/**生成指定的代理类。*/byte[]proxyClassFile=ProxyGenerator.generateProxyClass(proxyName,interfaces.toArray(EMPTY_CLASS_ARRAY),accessFlags);finalbyte[]classFile=gen.generateClassFile();addProxyMethod(hashCodeMethod,Object.class);addProxyMethod(equalsMethod,Object.class);addProxyMethod(toStringMethod,Object.class);methods.add(pm.generateMethod());最后通过asm实现的Cglib动态代理创建一个Enhancer对象。使用Enhancer对象设置代理类的父类(代理类)创建回调对象(回调类实现了MethodInterceptor接口)使用Enhancer对象设置回调对象使用Enhancer对象创建代理对象来创建拦截器,相当于JDK的InvocationHanderclassTimeMethodInterceptorimplementsMethodInterceptor{@OverridepublicObjectintercept(Objecto,Methodmethod,Object[]objects,MethodProxymethodProxy)throwsThrowable{System.out.println(o.getClass().getSuperclass().getName());System.out.println("之前");对象结果=空;结果=methodProxy.invokeSuper(o,objects);System.out.println("之后");返回结果;}}创建动态代理/***CGLIB不需要接口实现动态代理*/publicclassMain{publicstaticvoidmain(String[]args){Enhancerenhancer=newEnhancer();enhancer.setSuperclass(Tank.class);enhancer.setCallback(newTimeMethodInterceptor());坦克tank=(Tank)enhancer.create();坦克.移动();}}生成cglib代理的类,选择下面两个System.setProperty("cglib.debugLocation","./cg");System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"./cg");或者在idea->run->editconfigurations中设置vmoptions-Dcglib.debugLocation=./cg关于proxy的其他正向代理:知道目标地址,但是不能直接访问,需要通过代理才能访问这样如IP代理机场等。正向代理的过程隐藏了真正的客户端。客户端的请求服务由代理服务器代替请求。反向代理:如果不知道目标地址,代理服务器就相当于目标服务器。代理服务器收到所有请求后,将它们转发给其他内部服务器。比如访问百度www.baidu.com,却不知道实际访问的是哪个服务器。最全学习笔记大厂真题+微服务+MySQL+分布式+SSM框架+Java+Redis+数据结构与算法+网络+Linux+Spring全家桶+JVM+高并发+专业学习思维脑图+面试宝典