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

动态代理javanativevsCglib

时间:2023-04-01 16:12:08 Java

代理模式代理模式定义:为另一个对象提供代理或占位符,以控制对它的访问。也就是说,为对象提供一个代理来控制对它的访问。代理模式最广泛使用的场景是在不侵入代理对象的情况下增强代理对象的功能。它可以在不侵犯代理对象的情况下增强代理对象的功能。这种NB的特性使得代理模式在日常编码和各种框架中得到了广泛的应用。最NB的应用场景是AOP。动态代理是AOP最基本的底层技术逻辑。不管是哪一种AOP框架,其核心其实都是通过动态代理来实现的。动态代理代理的概念分为静态代理和动态代理。静态代理是指在代码编写过程中实现代理模式,相对来说非常简单。动态代理是指在运行时生成代理对象的字节码,从而在运行时改变或增强代理对象的行为。由于动态代理不需要在编码阶段硬编码代理模式的实现,具有很强的灵活性。JAVA中使用最广泛的动态代理实现有两种,一种是JDK原生动态代理实现,一种是通过Cglib实现。这两种方式的根本区别在于JDK的原生动态代理是基于接口实现的,运行时生成的代理对象是通过实现接口生成的。因此,代理对象也必须实现这个接口。cglib是基于继承实现的,所以对代理对象是否继承接口没有要求。运行时生成的代理对象是代理对象的子类对象。所以要求代理对象的代理方法不能是final的(final方法不能被扩展),并且代理对象必须有一个无参数的构造函数。如果不满足这两个要求,Cglig有不同的处理方式:final方法不能被代理,即使你创建一个Cglib代理对象,通过代理对象调用被代理对象的final方法,最终执行的还是被代理的对象方法,即方法不能被代理对象增强。因此,使用Cglib调用代理对象的final方法后,运行不会报错,但达不到增强方法的目的。但是,如果代理对象没有无参构造函数,Cglib代理对象在运行时就会抛出异常,程序无法运行。没有无参构造函数并不意味着没有显式定义无参构造函数,因为如果一个类没有任何构造函数的显式定义,这个类有一个无参构造函数,但是如果定义了一个有参构造函数,但是没有显式定义无参构造函数定义。在这种情况下,不存在真正意义上的无参数构造函数。我们以最简单的HelloWorld为例。假设场景是:HelloWorld有三个方法:hello、check和check1。方法很简单,打印一句话就可以了。要实现的目标是通过动态代理调用hello方法前后打印一句话,hello方法调用check方法,check方法调用check1。通过调用代理对象内部的check和chekc1方法,观察是否可以通过代理增强check和check1方法。JDK原生动态代理需要以下元素才能使用JDK原生动态代理:被代理对象(必须实现接口)。实现InvocationHandler接口的动态代理回调处理程序。代理对象。被代理对象:由于JDK原生动态代理要求被代理对象必须实现接口,所以首先定义接口:publicinterfaceHelloWorldInterface{voidhello();无效检查();voidcheck1();}被代理对象的实现类:publicclassHelloWroldImpimplementsHelloWorldInterface{@Overridepublicvoidhello(){System.out.println("HellofromProxyJDK1Imp");查看();}@Overridepublicvoidcheck(){System.out.println("我正在检查");检查1();}@Overridepublicvoidcheck1(){System.out.println("我是最后一个函数check1");}回调处理器回调处理器是针对代理对象的,代理对象执行时使用代理对象的接口方法时,会调用回调对象的invoke方法。实际动态代理的方法增强功能完全依赖回调处理器的invoke方法:回调对象中的目标是被代理对象,最终通过invoke方法调用被代理对象的原始方法,而在我们可以通过invoke方法实现增强功能。本例中的需求只是打印调用前后的日志,所以调用代理对象的方法前后可以分别输出日志。公共类JDKProxyHandle实现InvocationHandler{privateObjecttarget;publicJDKProxyHandle(Objecttarget){this.target=target;}@OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{System.out.println("beforeinvoke"+method.getName());方法调用(目标,参数);System.out.println("调用后"+method.getName());返回空值;方法.newProxyInstance创建一个代理对象。首先需要创建一个代理对象的实例,并将该实例作为参数传递给回调对象的初始化方法,从而创建一个回调对象。之后创建代理对象:publicstaticvoidmain(String[]args){HelloWorldInterfacehelloWorldInterface=newHelloWroldImp();InvocationHandlerhandler=newJDKProxyHandle(helloWorldInterface);HelloWorldInterfaceproxyHelloWorld=(HelloWorldInterface)Proxy.newProxyInstance(helloWorldInterface.getClass().getClassLoader(),helloWorldInterface.getClass().getInterfaces(),handler);}测试执行代理对象的hello方法:publicstaticvoidmain(String[]args){HelloWorldInterfacehelloWorldInterface=newHelloWroldImp();InvocationHandlerhandler=newJDKProxyHandle(helloWorldInterface);HelloWorldInterfaceproxyHelloWorld=(HelloWorldInterface)Proxy.newProxyInstance(helloWorldInterface.getClass().getClassLoader(),helloWorldInterface.getClass().getInterfaces(),handler);proxyHelloWorld.hello();proxyHelloWorld.check();}ProxyJDK1ImpinvokehelloHello之前的输出IamcheckIamlastfunctioncheck1afterinvokehellobeforeinvokecheckIamcheckIamlastfunctioncheck1afterinvokecheck从测试结果来看,我们可以得出以下结论:代理对象的hello()方法在内部被调用增强的hello()方法check()方法,check()方法内部调用check1()。check()和check1()方法都不能再次增强。check()方法单独通过代理对象调用,增强了check()方法。所以JDK原生动态代理的增强功能是不能在代理对象的内部调用中传递的!!!Cglib动态代理使用以下元素来使用Cglib动态代理:Proxied对象(不需要实现接口)。实现MethodInterceptor接口的拦截器。代理对象。代理对象:Cglib动态代理底层是通过扩展代理对象的子类实现的。因此被代理对象不需要实现接口,仍然使用上例中的HelloWorldImp作为被代理对象。MethodInterceptor拦截器的作用和JDK原生动态代理的回调是一样的。拦截器需要持有代理对象目标,实现回调方法拦截,并在拦截方法内部实现增强。公共类CglibInterceptor实现MethodInterceptor{私有对象目标;公共CglibInterceptor(对象目标){this.target=target;}@OverridepublicObjectintercept(Objecto,Methodmethod,Object[]objects,MethodProxymethodProxy)throwsThrowable{System.out.println("ThisiscglibProxybefore"+method.getName());methodProxy.invokeSuper(o,objects);System.out.println("ThisiscglibProxyafter"+method.getName());返回空值;}}代理对象以下创建代理对象的方法可以在客户端实现,也可以在拦截器内部实现,也可以创建一个Proxy创建工厂来获取。其中最重要的是Cglib增强器,它是实现Cglib动态代理的关键。增强器有两个最重要的成员变量,一个是父类,一个是回调对象。很容易理解,superclass就是代理对象的类对象,用来创建代理对象,回调对象就是我们上面创建的拦截器,用来在执行代理对象时回调,实现增强方法。//创建代理publicObjectcreateProxy(){//创建核心类Enhancerenhancer=newEnhancer();//设置父类enhancer.setSuperclass(target.getClass());//设置回调CglibInterceptorcli=newCglibInterceptor();enhancer.setCallback(cli);//生成代理对象proxy=enhancer.create();返回代理;}testpublicstaticvoidmain(String[]args){HelloWroldImphwi=newHelloWroldImp();CglibInterceptorcgi=newCglibInterceptor(hwi);HelloWroldImpproxyHwi=(HelloWroldImp)cgi.createProxy();proxyHwi.hello();}OutputThisiscglibProxybeforehelloHellofromProxyJDK1ImpThisiscglibProxybeforecheckIamcheckThisiscglibProxybeforecheck1Iamlastfunctioncheck1ThisiscglibProxyiscglibProxyaftercheckThisiscglibProxyafterhello从测试结果可以得出以下结论:()代理对象的方法得到增强。在hello()方法内部调用的check()方法在check()方法内部调用check1()。被重新增强。因此,Cglib动态代理的增强功能可以传递给代理对象的内部调用!!!