Cglib源码分析 invoke和invokeSuper的差别(转)
原文 https://blog.csdn.net/makecontral/article/details/79593732
Cglib的实例 本文重在源码的分析,Cglib的使用不再复述。 //被代理类 public class InfoDemo { public void welcome (String person){ System.out.println("welcome :" + person); } } public class CglibInfoProxy implements MethodInterceptor { private Object target; public Object newInstance(Object source){ target = source; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(this.target.getClass()); enhancer.setCallback(this); return enhancer.create(); } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("before method!!!"); Object value = methodProxy.invokeSuper(o, objects); //Object value = methodProxy.invoke(o, objects); return value; } public static void main(String[] args) { System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\\\classes"); InfoDemo instance = (InfoDemo) new CglibInfoProxy().newInstance(new InfoDemo()); instance.welcome("zhangsan"); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 代理类字节码分析 先来看main函数中new CglibInfoProxy().newInstance(new InfoDemo()),简单来说这个方法是将infoDemo作为一个父类,通过asm字节码生成一个子类来继承infoDemo。 InfoDemo$$EnhancerByCGLIB$$8b8da05b.class,就是生成的子类的字节码,称这个子类为InfoDemo的代理类。截取部分字节码显示: public class InfoDemo$$EnhancerByCGLIB$$8b8da05b extends InfoDemo implements Factory { private MethodInterceptor CGLIB$CALLBACK_0; private static final Method CGLIB$welcome$0$Method; private static final MethodProxy CGLIB$welcome$0$Proxy; //..........................................省略 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"); //..........................................省略 } final void CGLIB$welcome$0(String var1) { super.welcome(var1); } //先来判断这个代理类中是否设置了方法拦截。如果设置了就调用该拦截器的intercept方法。 //在本程序中,我们是设置了拦截器的。enhancer.setCallback(this); 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; } if(var10000 != null) { var10000.intercept(this, CGLIB$welcome$0$Method, new Object[]{var1}, CGLIB$welcome$0$Proxy); } else { super.welcome(var1); } } //..........................................省略 } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 在这个代理类中,这里有两点需要注意。 1.它会将父类中的每一个方法,生成两个与之对应。如父类中的welcome,在代理类中就会有CGLIBwelcomewelcome0,welcome的两个方法与之对应。 2.每一个方法都会静态块中,经过MethodProxy.create生成对应的方法代理。如 MethodProxy.create(var1, var0, "(Ljava/lang/String;)V", "welcome", "CGLIB$welcome$0"); 当main函数执行到instance.welcome(“zhangsan”);这个语句时,会进入到代理类中的public final void welcome(String var1) 方法。进而执行了var10000.intercept()方法。 根据CglibInfoProxy中的intercept,先是会输出一句“before method!!!”,然后调用methodProxy.invokeSuper(o, objects); 这里的methodProxy对应的是var10000.intercept(this, CGLIB$welcome$0$Method, new Object[]{var1}, CGLIB$welcome$0$Proxy)中的CGLIB$welcome$0$Proxy。 那么就相当于执行CGLIB$welcome$0$Proxy.invokeSuper(o, objects),那么来看看invokeSuper在做什么。 MethodProxy 源码分析 public class MethodProxy { //下面的前三个变量在create方法中,都已经得到了初始值了。 private Signature sig1; private Signature sig2; private MethodProxy.CreateInfo createInfo; //FastClassInfo是在调用methodProxy.invoke或者methodProxy.invokeSuper中,init()会触发,后面再来细看这个。 private volatile MethodProxy.FastClassInfo fastClassInfo; public Object invokeSuper(Object obj, Object[] args) throws Throwable { try { this.init(); MethodProxy.FastClassInfo fci = this.fastClassInfo; 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; 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; } } } //本质就是要生成一个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不一样。如果缓存中就取出,没有就生成新的FastClass fci.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; } } } } //根据一个类的信息,返回的该对象的一个Fastclass private 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(); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 init()的过程就是生成FastClassInfo。 对于FastClass可以看看这篇文章https://www.cnblogs.com/cruze/p/3865180.html fci.f2.invoke(fci.i2, obj, args);fci存放了两个类的fastClass。 其中f1是被代理的类对应的是InfoDemo$$FastClassByCGLIB$$f4c7f3ac.class, f2InfoDemo$$EnhancerByCGLIB$$8b8da05b$$FastClassByCGLIB$$9065a6c.class 这也就是为什么,在classes文件中会生成三个class文件了,一个代理类,两个fastclass. f2.invoke,那就说明会调用InfoDemo$$EnhancerByCGLIB$$8b8da05b$$FastClassByCGLIB$$9065a6c这个类的方法。 代理类的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; } } //----省略 } 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: var10000.CGLIB$welcome$0((String)var3[0]); } //----省略 } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 可以看到,会执行到voke方法中的 var10000.CGLIB$welcome$0((String)var3[0]);,而CGLIB$welcome$0其实就是直接调用了super.welcome(var1)的方法。输出结束之后就会运行完毕。 那么,如果现在调用的是methodProxy.invoke(o, objects);而不是invokeSuper会是怎么样的情况呢? 通过上面MethodProxy的源码,可以看到当执行invoke的时候会执行到fci.f1.invoke(fci.i1, obj, args); 即会调用被代理类的fastclass,InfoDemo$$FastClassByCGLIB$$f4c7f3ac.class中的invoke。 打开该class文件我们会发现,执行的的是welcome()。 public Object invoke(Object obj, Object[] args) throws Throwable { try { switch(var10001) { case 0: var10000.welcome((String)var3[0]); return null; case 1: } } 1 2 3 4 5 6 7 8 9 那不就是和在main函数中instance.welcome(“zhangsan”)一样的步骤了嘛,就会又开始循环调用,一直到栈溢出报错。所以,invoke会造成OOM的问题。