一、CGLIB动态代理
JDK动态代理要求被代理的类必须实现接口,有很强的局限性,而CGLIB动态代理则不要求被代理类实现接口。简单的说,CGLIB会让生成的代理类继承被代理类,并在代理类中对代理方法进行强化处理(前置处理、后置处理等)。在CGLIB底层,其实是借助了ASM这个非常强大的Java字节码生成框架。Cglib无法代理被final修饰的方法。
cglib的原理是继承,cglib通过继承目标类,创建它的子类,在子类中重写父类中同名的方法,实现功能的修改。因为cglib是继承,重写方法,所以要求目标类不能是final的,方法也不能是final的。cglib的要求目标类比较宽松,只要能继承就可以了。cglib在很多的框架中使用,比如mybatis,spring框架中都有使用。
1、cglib动态代理的简单使用
使用CGLIB动态代理主要有以下几个步骤:
1.编写一个被代理类(也称委托类或目标类),无需实现任何接口;
2.自定义一个方法拦截器,实现MethodInterceptor接口,并重写intercept方法;
3.在intercept方法中调用MethodProxy类的invokeSuper方法(而不是调用invoke方法,因为invoke会引起死循环,导致堆栈内存溢出,具体原因在下面会详细分析。在invokeSuper方法中,底层实际上最终会调用被代理类中相应的被代理方法);
4.创建代理对象,并调用被代理类中的方法(实际上是调用代理对象中重写的代理方法)。
示例代码:
package com.tx.study.others.proxy.cglibProxy;/*** @Author: 倚天照海* @Description: 被代理类*/
public class InfoDemo {//被代理方法(被final修饰的方法无法被代理)public void welcome (String person){System.out.println("welcome :" + person);}}
package com.tx.study.others.proxy.cglibProxy;import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;import java.lang.reflect.Method;/*** @Author: 倚天照海* @Description: 自定义方法拦截器,实现MethodInterceptor接口,并重写intercept方法*/
public class MyMethodInterceptor implements MethodInterceptor {/*** 重写intercept方法** @param o 代理对象* @param method 被代理方法对应的Method对象* @param objects 被代理方法的参数* @param methodProxy 方法代理对象* @return 被代理方法的返回值* @throws Throwable 可能会抛出的异常*/@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {//在真实的对象执行之前可以添加自己的操作,插入前置通知System.out.println("before method!!!");//在invokeSuper方法中最终会调用被代理类中对应的被代理方法Object value = methodProxy.invokeSuper(o, objects);//会引起死循环,导致内存溢出//Object value = methodProxy.invoke(o, objects);//在真实的对象执行之后可以添加自己的操作,插入后置通知System.out.println("after method!!!");return value;}}
package com.tx.study.others.proxy.cglibProxy;import org.springframework.cglib.core.DebuggingClassWriter;
import org.springframework.cglib.proxy.Enhancer;/*** @Author: 倚天照海* @Description: cglib动态代理测试类*/
public class CglibProxyTest {/*** 该方法的作用就是封装获取代理对象的代码,即获取代理对象* @return 代理对象*/public static Object getProxyInstance() {//Enhancer是CGLIB的字节码增强器,可以很方便的对类进行拓展Enhancer enhancer = new Enhancer();//为代理类设置父类,即代理类继承被代理类enhancer.setSuperclass(InfoDemo.class);//为代理类设置回调对象,即自定义的方法拦截器enhancer.setCallback(new MyMethodInterceptor());//创建代理对象return enhancer.create();}public static void main(String[] args) {//将代理类class文件存入本地磁盘,方便反编译查看源码String path = "D:\\ProgramFiles\\workspace\\zznode\\data-query\\tx-study\\src\\main\\java\\com\\tx\\study\\others\\proxy\\cglibProxy\\";System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, path);//获取代理对象InfoDemo instance = (InfoDemo) getProxyInstance();//调用被代理方法(底层会调用自定义方法拦截器中重写的intercept方法)instance.welcome("zhangsan");}}
运行结果:
before method!!!
welcome :zhangsan
after method!!!
2、cglib动态代理的原理
原理分析:
执行测试类,会生成三个class文件,如下所示,通过反编译可以看到这三个文件中的源码。
InfoDemo$$EnhancerByCGLIB$$8b8da05b.class (cglib生成的代理类,继承InfoDemo类)
InfoDemo$$FastClassByCGLIB$$f4c7f3ac.class (被代理类的FastClass,记为f1)
InfoDemo$$EnhancerByCGLIB$$8b8da05b$$FastClassByCGLIB$$9065a6c.class (代理类的FastClass,记为f2)(在下面会详细说明这三个类的部分源码)
在MethodProxy类中有一个静态内部类FastClassInfo,如下所示。FastClassInfo中有四个属性,FastClass f1表示被代理类的FastClass,FastClass f2表示代理类的FastClass,int i1表示被代理类的方法签名(实际上是方法对应的索引,根据该索引可以快速找到对应的方法,这就是cglib动态代理比JDK动态代理效率高的主要原因),int i2表示代理类的方法签名。通过FastClassInfo对象可以得到被代理类和代理类的FastClass,在下面会详细介绍。
private static class FastClassInfo {FastClass f1;//被代理类的FastClassFastClass f2;//代理类的FastClassint i1; //被代理类的方法签名(index)int i2;//代理类的方法签名private FastClassInfo() {}
}
先看一下本例中生成的代理类(InfoDemo$$EnhancerByCGLIB$$8b8da05b.class)的源码。
代理类InfoDemo$$EnhancerByCGLIB$$8b8da05b.class:
public class InfoDemo$$EnhancerByCGLIB$$8b8da05b extends InfoDemo implements Factory {private MethodInterceptor CGLIB$CALLBACK_0; //拦截器private static final Method CGLIB$welcome$0$Method; //被代理方法(是Method对象)private static final MethodProxy CGLIB$welcome$0$Proxy; //方法代理(MethodProxy对象)//..........................................省略static void CGLIB$STATICHOOK1() {CGLIB$THREAD_CALLBACKS = new ThreadLocal();CGLIB$emptyArgs = new Object[0];Class var0 = Class.forName("CglibTest.InfoDemo$$EnhancerByCGLIB$$8b8da05b");Class var1;//通过反射获取被代理方法CGLIB$welcome$0$Method = ReflectUtils.findMethods(new String[]{"welcome", "(Ljava/lang/String;)V"}, (var1 = Class.forName("CglibTest.InfoDemo")).getDeclaredMethods())[0];//生成与被代理方法对应的方法代理对象CGLIB$welcome$0$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;)V", "welcome", "CGLIB$welcome$0");//..........................................省略}//cglib会生成与被代理方法对应的代理方法CGLIB$welcome$0final void CGLIB$welcome$0(String var1) {//直接调用父类的被代理方法(实际上被代理方法就是在此处被调用的)super.welcome(var1);}//由于代理类继承了被代理类,所以在代理类中生成了被代理方法(进行了重写)public final void welcome(String var1) {MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;if(this.CGLIB$CALLBACK_0 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_0;}//判断代理类中是否设置了方法拦截,如果设置了就调用该拦截器的intercept方法//在本例中设置了拦截器enhancer.setCallback(new MyMethodInterceptor());if(var10000 != null) {var10000.intercept(this, CGLIB$welcome$0$Method, new Object[]{var1}, CGLIB$welcome$0$Proxy);} else {//如果没有设置拦截器,就直接调用父类的被代理方法super.welcome(var1);}}//..........................................省略
}
在代理类中会生成与父类(即被代理类)中每一个方法相对应的两个方法,例如此例中父类的welcome方法,在代理类中生成了CGLIB$welcome$0代理方法和重写的welcome方法。另外,父类中的每一个方法都会在静态块中,通过MethodProxy.create生成对应的方法代理。
在本例测试类中,代理对象调用父类的被代理方法,即instance.welcome("zhangsan"),实际上调用的是代理类中被重写后的welcome方法,即public final void welcome(String var1)。在该方法中调用了拦截器MethodInterceptor的intercept方法。由于自定义的方法拦截器实现了MethodInterceptor接口,并对intercept方法进行了重写,所以,实际上调用的是重写后的intercept方法。在重写的intercept方法中调用了MethodProxy对象的invokeSuper(o, objects)方法,接下来重点看一下invokeSuper方法的执行过程,即分析MethodProxy的源码。
先看一下代理类中调用的intercept(this, CGLIB$welcome$0$Method, new Object[]{var1}, CGLIB$welcome$0$Proxy)方法,该方法中有四个参数,第一个参数是this,即调用代理类中重写后的welcome方法的对象,当然是代理对象了。第二个参数是被代理方法(实际上是Method对象),第三个参数是被代理方法的参数数组,第四个参数是方法代理对象(即MethodProxy对象)。这四个参数也就是自定义方法拦截器的intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy)方法中的四个参数。所以,methodProxy.invokeSuper(o, objects)方法中的methodProxy就是方法代理对象,o就是代理对象,objects就是被代理方法的参数。
MethodProxy 源码分析:
public class MethodProxy {//下面的前三个变量在create方法中,都已经得到了初始值了。private Signature sig1;private Signature sig2;private MethodProxy.CreateInfo createInfo;//在调用invoke或者invokeSuper中,通过init()方法生成FastClassInfoprivate volatile MethodProxy.FastClassInfo fastClassInfo;public Object invokeSuper(Object obj, Object[] args) throws Throwable {try {this.init();MethodProxy.FastClassInfo fci = this.fastClassInfo;//调用代理类的FastClass的invoke方法return fci.f2.invoke(fci.i2, obj, args);} catch (InvocationTargetException var4) {throw var4.getTargetException();}}public Object invoke(Object obj, Object[] args) throws Throwable {try {this.init();MethodProxy.FastClassInfo fci = this.fastClassInfo;//调用被代理类的FastClass的invoke方法return fci.f1.invoke(fci.i1, obj, args);} catch (InvocationTargetException var4) {throw var4.getTargetException();} catch (IllegalArgumentException var5) {if(this.fastClassInfo.i1 < 0) {throw new IllegalArgumentException("Protected method: " + this.sig1);} else {throw var5;}}}//init方法就是为了生成FastClassInfo,FastClassInfo中存放着两个fastclass(f1、f2)和两个方法索引的值(i1、i2)。private void init() {if(this.fastClassInfo == null) {Object var1 = this.initLock;synchronized(this.initLock) {if(this.fastClassInfo == null) {MethodProxy.CreateInfo ci = this.createInfo;MethodProxy.FastClassInfo fci = new MethodProxy.FastClassInfo();//不是每一个方法都生成一个fastclass,每一个方法的fastclass都是一样的,//只不过他们的i1,i2不一样。如果缓存中有就取出,没有就生成新的FastClassfci.f1 = helper(ci, ci.c1);fci.f2 = helper(ci, ci.c2);fci.i1 = fci.f1.getIndex(this.sig1);fci.i2 = fci.f2.getIndex(this.sig2);this.fastClassInfo = fci;this.createInfo = null;}}}}//根据一个类的信息,返回该对象的一个Fastclassprivate static FastClass helper(MethodProxy.CreateInfo ci, Class type) {Generator g = new Generator();g.setType(type);g.setClassLoader(ci.c2.getClassLoader());g.setNamingPolicy(ci.namingPolicy);g.setStrategy(ci.strategy);g.setAttemptLoad(ci.attemptLoad);return g.create();}//FastClassInfo是MethodProxy的静态内部类private static class FastClassInfo {FastClass f1;//被代理类的FastClassFastClass f2;//代理类的FastClassint i1; //被代理类的方法签名(index)int i2;//代理类的方法签名private FastClassInfo() {}}
}
在MethodProxy类的invokeSuper方法中调用了代理类的FastClass的invoke方法,接下来看一下代理类的FastClass的源码。代理类的FastClass的字节码反编译后的源码如下所示。
代理类的FastClass:
代理类的FastClass(InfoDemo$$EnhancerByCGLIB$$8b8da05b$$FastClassByCGLIB$$9065a6c)的字节码反编译后的源码:
public class InfoDemo$$EnhancerByCGLIB$$8b8da05b$$FastClassByCGLIB$$9065a6c extends FastClass {public InfoDemo$$EnhancerByCGLIB$$8b8da05b$$FastClassByCGLIB$$9065a6c(Class var1) {super(var1);}public int getIndex(Signature var1) {String var10000 = var1.toString();switch(var10000.hashCode()) {case -2055565910:if(var10000.equals("CGLIB$SET_THREAD_CALLBACKS([Lnet/sf/cglib/proxy/Callback;)V")) {return 12;}break;case -1725733088:if(var10000.equals("getClass()Ljava/lang/Class;")) {return 24;}case 1013143764:if(var10000.equals("CGLIB$welcome$0(Ljava/lang/String;)V")) {return 17;}}//----省略}//在MethodProxy类的invokeSuper方法中调用了此处的invoke方法public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {8b8da05b var10000 = (8b8da05b)var2;int var10001 = var1;try {switch(var10001) {case 0:return var10000.toString();case 1:return new Integer(var10000.hashCode());case 17://调用代理对象的代理方法CGLIB$welcome$0var10000.CGLIB$welcome$0((String)var3[0]);}//----省略}}
}
由上述代码可知,在代理类的FastClass的invoke方法中调用了代理对象的CGLIB$welcome$0方法,由上面分析的代理类的源码可知,在代理类的CGLIB$welcome$0方法中直接调用父类的被代理方法,即调用此例中InfoDemo类的welcome方法,执行被代理方法。至此,cglib动态代理的大致过程就分析完毕了。
如果在自定义方法拦截器中调用的不是methodProxy.invokeSuper(o, objects)方法,而是methodProxy.invoke(o, objects)方法,为什么会引起死循环,导致内存溢出呢?由MethodProxy的源码可知,在MethodProxy的invoke方法中调用了被代理类的FastClass的invoke方法,所以,接下来看一下被代理类的FastClass(InfoDemo$$FastClassByCGLIB$$f4c7f3ac.class)被反编译后的部分源码。
public class InfoDemo$$FastClassByCGLIB$$f4c7f3ac extends FastClass {
public Object invoke(Object obj, Object[] args) throws Throwable {try {switch(var10001) {case 0://调用被代理对象的被代理方法welcomevar10000.welcome((String)var3[0]);return null;case 1:}}
}
}
在被代理类的FastClass的invoke方法中调用被代理对象的welcome方法,通过代理对象调用被代理方法,与在main函数中instance.welcome(“zhangsan”)是一样的步骤,即又从头开始循环调用,直到栈内存溢出。