前提 前一篇文章介绍了CGLIB中常用的API,实际上使用了Enhancer
和MethodInterceptor
之后会生成代理子类,这篇文章就是分析一下CGLIB动态代理的原理。
CGLIB动态代理原理分析 我们经常说CGLIB的动态代理的底层通过被代理类生成代理子类实现的,那么下面我们就分析一下生成的子类到底是什么样的。开启CGLIB的debug模式,输出它生成的类到指定的目录:
public class DebuggingCglibDemo { private static final String METHOD_NAME = "sayHello" ; public static void main (String[] args) throws Exception { String location = DebuggingCglibDemo.class.getResource("" ).getPath() + "debugging/" ; System.out.println("location -> " + location); System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, location); Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(SampleClass.class); enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept (Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { Object result; if (METHOD_NAME.equals(method.getName())) { System.out.println("Before invoking sayHello..." ); result = methodProxy.invokeSuper(obj, objects); System.out.println("After invoking sayHello..." ); } else { result = methodProxy.invokeSuper(obj, objects); } return result; } }); SampleClass sampleClass = (SampleClass) enhancer.create(); System.out.println(sampleClass.sayHello("throwable" )); } }
输出结果:
location -> /D:/Projects/cglib-seed/target/classes/club/throwable/cglib/debugging/ CGLIB debugging enabled, writing to '/D:/Projects/cglib-seed/target/classes/club/throwable/cglib/debugging/' Before invoking sayHello... After invoking sayHello... throwable say hello!
这个时候,看下target下面生成的类如下:
一共有五个类:
../net.sf.cglib
包下:
MethodWrapper$MethodWrapperKey$$KeyFactoryByCGLIB$$d45e49f7.class
Enhancer$EnhancerKey$$KeyFactoryByCGLIB$$7fb24d72.class
这两个类主要很缓存的Key相关,这里不做详细展开。
用户自定义包../club/throwable/cglib
包下:
SampleClass$$EnhancerByCGLIB$$53c7afed$$FastClassByCGLIB$$da5c8621.class
SampleClass$$EnhancerByCGLIB$$53c7afed.class
SampleClass$$FastClassByCGLIB$$cf1a549b.class
这三个就是实际使用到的子类,其中有一个是被代理类的直接子类SampleClass$$EnhancerByCGLIB$$53c7afed
,其他的两个是FastClass
。
接着我们先看一下SampleClass$$EnhancerByCGLIB$$53c7afed这个类的源码:
与Java原生代理类似,仍然以静态变量保存了指向代理方法的引用 private boolean CGLIB$BOUND; public static Object CGLIB$FACTORY_DATA; private static final ThreadLocal CGLIB$THREAD_CALLBACKS; private static final Callback[] CGLIB$STATIC_CALLBACKS; private MethodInterceptor CGLIB$CALLBACK_0; private static Object CGLIB$CALLBACK_FILTER; private static final Method CGLIB$sayHello$0 $Method; private static final MethodProxy CGLIB$sayHello$0 $Proxy; private static final Object[] CGLIB$emptyArgs; private static final Method CGLIB$equals$1 $Method; private static final MethodProxy CGLIB$equals$1 $Proxy; private static final Method CGLIB$toString$2 $Method; private static final MethodProxy CGLIB$toString$2 $Proxy; private static final Method CGLIB$hashCode$3 $Method; private static final MethodProxy CGLIB$hashCode$3 $Proxy; private static final Method CGLIB$clone$4 $Method; private static final MethodProxy CGLIB$clone$4 $Proxy; static void CGLIB$STATICHOOK1() { CGLIB$THREAD_CALLBACKS = new ThreadLocal(); CGLIB$emptyArgs = new Object[0 ]; Class var0 = Class.forName("club.throwable.cglib.SampleClass$$EnhancerByCGLIB$$53c7afed" ); Class var1; Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals" , "(Ljava/lang/Object;)Z" , "toString" , "()Ljava/lang/String;" , "hashCode" , "()I" , "clone" , "()Ljava/lang/Object;" }, (var1 = Class.forName("java.lang.Object" )).getDeclaredMethods()); CGLIB$equals$1 $Method = var10000[0 ]; CGLIB$equals$1 $Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z" , "equals" , "CGLIB$equals$1" ); CGLIB$toString$2 $Method = var10000[1 ]; CGLIB$toString$2 $Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;" , "toString" , "CGLIB$toString$2" ); CGLIB$hashCode$3 $Method = var10000[2 ]; CGLIB$hashCode$3 $Proxy = MethodProxy.create(var1, var0, "()I" , "hashCode" , "CGLIB$hashCode$3" ); CGLIB$clone$4 $Method = var10000[3 ]; CGLIB$clone$4 $Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;" , "clone" , "CGLIB$clone$4" ); CGLIB$sayHello$0 $Method = ReflectUtils.findMethods(new String[]{"sayHello" , "(Ljava/lang/String;)Ljava/lang/String;" }, (var1 = Class.forName("club.throwable.cglib.SampleClass" )).getDeclaredMethods())[0 ]; CGLIB$sayHello$0 $Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;)Ljava/lang/String;" , "sayHello" , "CGLIB$sayHello$0" ); } final String CGLIB$sayHello$0 (String var1) { return super .sayHello(var1); } public final String sayHello (String var1) { MethodInterceptor var10000 = this .CGLIB$CALLBACK_0; if (this .CGLIB$CALLBACK_0 == null ) { CGLIB$BIND_CALLBACKS(this ); var10000 = this .CGLIB$CALLBACK_0; } return var10000 != null ? (String)var10000.intercept(this , CGLIB$sayHello$0 $Method, new Object[]{var1}, CGLIB$sayHello$0 $Proxy) : super .sayHello(var1); } final boolean CGLIB$equals$1 (Object var1) { return super .equals(var1); } public final boolean equals (Object 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 ) { Object var2 = var10000.intercept(this , CGLIB$equals$1 $Method, new Object[]{var1}, CGLIB$equals$1 $Proxy); return var2 == null ? false : (Boolean)var2; } else { return super .equals(var1); } } final String CGLIB$toString$2 () { return super .toString(); } public final String toString () { MethodInterceptor var10000 = this .CGLIB$CALLBACK_0; if (this .CGLIB$CALLBACK_0 == null ) { CGLIB$BIND_CALLBACKS(this ); var10000 = this .CGLIB$CALLBACK_0; } return var10000 != null ? (String)var10000.intercept(this , CGLIB$toString$2 $Method, CGLIB$emptyArgs, CGLIB$toString$2 $Proxy) : super .toString(); } final int CGLIB$hashCode$3 () { return super .hashCode(); } public final int hashCode () { MethodInterceptor var10000 = this .CGLIB$CALLBACK_0; if (this .CGLIB$CALLBACK_0 == null ) { CGLIB$BIND_CALLBACKS(this ); var10000 = this .CGLIB$CALLBACK_0; } if (var10000 != null ) { Object var1 = var10000.intercept(this , CGLIB$hashCode$3 $Method, CGLIB$emptyArgs, CGLIB$hashCode$3 $Proxy); return var1 == null ? 0 : ((Number)var1).intValue(); } else { return super .hashCode(); } } final Object CGLIB$clone$4 () throws CloneNotSupportedException { return super .clone(); } protected final Object clone () throws CloneNotSupportedException { MethodInterceptor var10000 = this .CGLIB$CALLBACK_0; if (this .CGLIB$CALLBACK_0 == null ) { CGLIB$BIND_CALLBACKS(this ); var10000 = this .CGLIB$CALLBACK_0; } return var10000 != null ? var10000.intercept(this , CGLIB$clone$4 $Method, CGLIB$emptyArgs, CGLIB$clone$4 $Proxy) : super .clone(); } public static MethodProxy CGLIB$findMethodProxy(Signature var0) { String var10000 = var0.toString(); switch (var10000.hashCode()) { case -1816210712 : if (var10000.equals("sayHello(Ljava/lang/String;)Ljava/lang/String;" )) { return CGLIB$sayHello$0 $Proxy; } break ; case -508378822 : if (var10000.equals("clone()Ljava/lang/Object;" )) { return CGLIB$clone$4 $Proxy; } break ; case 1826985398 : if (var10000.equals("equals(Ljava/lang/Object;)Z" )) { return CGLIB$equals$1 $Proxy; } break ; case 1913648695 : if (var10000.equals("toString()Ljava/lang/String;" )) { return CGLIB$toString$2 $Proxy; } break ; case 1984935277 : if (var10000.equals("hashCode()I" )) { return CGLIB$hashCode$3 $Proxy; } } return null ; } public SampleClass$$EnhancerByCGLIB$$53c7afed() { CGLIB$BIND_CALLBACKS(this ); } public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] var0) { CGLIB$THREAD_CALLBACKS.set(var0); } public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] var0) { CGLIB$STATIC_CALLBACKS = var0; } private static final void CGLIB$BIND_CALLBACKS(Object var0) { SampleClass$$EnhancerByCGLIB$$53c7afed var1 = (SampleClass$$EnhancerByCGLIB$$53c7afed)var0; if (!var1.CGLIB$BOUND) { var1.CGLIB$BOUND = true ; Object var10000 = CGLIB$THREAD_CALLBACKS.get(); if (var10000 == null ) { var10000 = CGLIB$STATIC_CALLBACKS; if (CGLIB$STATIC_CALLBACKS == null ) { return ; } } var1.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])var10000)[0 ]; } } public Object newInstance (Callback[] var1) { CGLIB$SET_THREAD_CALLBACKS(var1); SampleClass$$EnhancerByCGLIB$$53c7afed var10000 = new SampleClass$$EnhancerByCGLIB$$53c7afed(); CGLIB$SET_THREAD_CALLBACKS((Callback[])null ); return var10000; } public Object newInstance (Callback var1) { CGLIB$SET_THREAD_CALLBACKS(new Callback[]{var1}); SampleClass$$EnhancerByCGLIB$$53c7afed var10000 = new SampleClass$$EnhancerByCGLIB$$53c7afed(); CGLIB$SET_THREAD_CALLBACKS((Callback[])null ); return var10000; } public Object newInstance (Class[] var1, Object[] var2, Callback[] var3) { CGLIB$SET_THREAD_CALLBACKS(var3); SampleClass$$EnhancerByCGLIB$$53c7afed var10000 = new SampleClass$$EnhancerByCGLIB$$53c7afed; switch (var1.length) { case 0 : var10000.<init>(); CGLIB$SET_THREAD_CALLBACKS((Callback[])null ); return var10000; default : throw new IllegalArgumentException("Constructor not found" ); } } public Callback getCallback (int var1) { CGLIB$BIND_CALLBACKS(this ); MethodInterceptor var10000; switch (var1) { case 0 : var10000 = this .CGLIB$CALLBACK_0; break ; default : var10000 = null ; } return var10000; } public void setCallback (int var1, Callback var2) { switch (var1) { case 0 : this .CGLIB$CALLBACK_0 = (MethodInterceptor)var2; default : } } public Callback[] getCallbacks() { CGLIB$BIND_CALLBACKS(this ); return new Callback[]{this .CGLIB$CALLBACK_0}; } public void setCallbacks (Callback[] var1) { this .CGLIB$CALLBACK_0 = (MethodInterceptor)var1[0 ]; } static { CGLIB$STATICHOOK1(); } }
这个类十分长,因为里面有很多的变量,它们都通过了静态代码块使用反射初始化。类的代码比JDK动态代理的子类多,因此生成效率会比较低。相关比较重要的注释已经写在类中,我们最主要关注两点:
private static final Method CGLIB$sayHello$0 $Method;private static final MethodProxy CGLIB$sayHello$0 $Proxy;
这两个静态变量都是指向sayHello
这个方法,CGLIB$sayHello$0$Method
直接指向父类方法,CGLIB$sayHello$0$Proxy
是CGLIB生成的方法代理。
final String CGLIB$sayHello$0 (String var1) { return super .sayHello(var1); } public final String sayHello (String var1) { MethodInterceptor var10000 = this .CGLIB$CALLBACK_0; if (this .CGLIB$CALLBACK_0 == null ) { CGLIB$BIND_CALLBACKS(this ); var10000 = this .CGLIB$CALLBACK_0; } return var10000 != null ? (String)var10000.intercept(this , CGLIB$sayHello$0 $Method, new Object[]{var1}, CGLIB$sayHello$0 $Proxy) : super .sayHello(var1); }
也就是如果想要启用CGLIB的回调,我们主观上应该是这样操作的:
SampleClass$$EnhancerByCGLIB$$53c7afed sample = new SampleClass$$EnhancerByCGLIB$$53c7afed(); sample.sayHello("doge" );
但是由于这个代理类是动态生成的,只能通过反射调用。
那么,剩下的两个FastClass
的作用是什么?我们先看一下MethodProxy
的invoke()
和invokeSuper()
方法:
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(); 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 ; } } } } 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; } } } 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(); } }
这里,两个方法各自使用了不同的FastClassInfo
实例fci.f2
和fci.f2
。其中,SampleClass$$FastClassByCGLIB$$cf1a549b.class
是对应于父类,而SampleClass$$EnhancerByCGLIB$$53c7afed$$FastClassByCGLIB$$da5c8621
是对应于CGLIB生成的被代理类的子类。下面展开SampleClass$$EnhancerByCGLIB$$53c7afed$$FastClassByCGLIB$$da5c8621
的源码:
public class SampleClass $$EnhancerByCGLIB $$53c7afed $$FastClassByCGLIB $$da5c8621 extends FastClass { public SampleClass$$EnhancerByCGLIB$$53c7afed$$FastClassByCGLIB$$da5c8621(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 10 ; } break ; case -1882565338 : if (var10000.equals("CGLIB$equals$1(Ljava/lang/Object;)Z" )) { return 18 ; } break ; case -1816210712 : if (var10000.equals("sayHello(Ljava/lang/String;)Ljava/lang/String;" )) { return 7 ; } break ; case -1457535688 : if (var10000.equals("CGLIB$STATICHOOK1()V" )) { return 15 ; } break ; case -1411842725 : if (var10000.equals("CGLIB$hashCode$3()I" )) { return 19 ; } break ; case -894172689 : if (var10000.equals("newInstance(Lnet/sf/cglib/proxy/Callback;)Ljava/lang/Object;" )) { return 5 ; } break ; case -623122092 : if (var10000.equals("CGLIB$findMethodProxy(Lnet/sf/cglib/core/Signature;)Lnet/sf/cglib/proxy/MethodProxy;" )) { return 14 ; } break ; case -508378822 : if (var10000.equals("clone()Ljava/lang/Object;" )) { return 3 ; } break ; case -419626537 : if (var10000.equals("setCallbacks([Lnet/sf/cglib/proxy/Callback;)V" )) { return 13 ; } break ; case 560567118 : if (var10000.equals("setCallback(ILnet/sf/cglib/proxy/Callback;)V" )) { return 8 ; } break ; case 811063227 : if (var10000.equals("newInstance([Ljava/lang/Class;[Ljava/lang/Object;[Lnet/sf/cglib/proxy/Callback;)Ljava/lang/Object;" )) { return 6 ; } break ; case 973717575 : if (var10000.equals("getCallbacks()[Lnet/sf/cglib/proxy/Callback;" )) { return 11 ; } break ; case 1221173700 : if (var10000.equals("newInstance([Lnet/sf/cglib/proxy/Callback;)Ljava/lang/Object;" )) { return 4 ; } break ; case 1230699260 : if (var10000.equals("getCallback(I)Lnet/sf/cglib/proxy/Callback;" )) { return 12 ; } break ; case 1298742135 : if (var10000.equals("CGLIB$sayHello$0(Ljava/lang/String;)Ljava/lang/String;" )) { return 16 ; } break ; case 1306468936 : if (var10000.equals("CGLIB$toString$2()Ljava/lang/String;" )) { return 20 ; } break ; case 1584330438 : if (var10000.equals("CGLIB$SET_STATIC_CALLBACKS([Lnet/sf/cglib/proxy/Callback;)V" )) { return 9 ; } break ; case 1800494055 : if (var10000.equals("CGLIB$clone$4()Ljava/lang/Object;" )) { return 17 ; } break ; case 1826985398 : if (var10000.equals("equals(Ljava/lang/Object;)Z" )) { return 0 ; } break ; case 1913648695 : if (var10000.equals("toString()Ljava/lang/String;" )) { return 1 ; } break ; case 1984935277 : if (var10000.equals("hashCode()I" )) { return 2 ; } } return -1 ; } public int getIndex (String var1, Class[] var2) { switch (var1.hashCode()) { case -2012993625 : if (var1.equals("sayHello" )) { switch (var2.length) { case 1 : if (var2[0 ].getName().equals("java.lang.String" )) { return 7 ; } } } break ; case -1983192202 : if (var1.equals("CGLIB$sayHello$0" )) { switch (var2.length) { case 1 : if (var2[0 ].getName().equals("java.lang.String" )) { return 16 ; } } } break ; case -1776922004 : if (var1.equals("toString" )) { switch (var2.length) { case 0 : return 1 ; } } break ; case -1295482945 : if (var1.equals("equals" )) { switch (var2.length) { case 1 : if (var2[0 ].getName().equals("java.lang.Object" )) { return 0 ; } } } break ; case -1053468136 : if (var1.equals("getCallbacks" )) { switch (var2.length) { case 0 : return 11 ; } } break ; case -124978609 : if (var1.equals("CGLIB$equals$1" )) { switch (var2.length) { case 1 : if (var2[0 ].getName().equals("java.lang.Object" )) { return 18 ; } } } break ; case -60403779 : if (var1.equals("CGLIB$SET_STATIC_CALLBACKS" )) { switch (var2.length) { case 1 : if (var2[0 ].getName().equals("[Lnet.sf.cglib.proxy.Callback;" )) { return 9 ; } } } break ; case -29025555 : if (var1.equals("CGLIB$hashCode$3" )) { switch (var2.length) { case 0 : return 19 ; } } break ; case 85179481 : if (var1.equals("CGLIB$SET_THREAD_CALLBACKS" )) { switch (var2.length) { case 1 : if (var2[0 ].getName().equals("[Lnet.sf.cglib.proxy.Callback;" )) { return 10 ; } } } break ; case 94756189 : if (var1.equals("clone" )) { switch (var2.length) { case 0 : return 3 ; } } break ; case 147696667 : if (var1.equals("hashCode" )) { switch (var2.length) { case 0 : return 2 ; } } break ; case 161998109 : if (var1.equals("CGLIB$STATICHOOK1" )) { switch (var2.length) { case 0 : return 15 ; } } break ; case 495524492 : if (var1.equals("setCallbacks" )) { switch (var2.length) { case 1 : if (var2[0 ].getName().equals("[Lnet.sf.cglib.proxy.Callback;" )) { return 13 ; } } } break ; case 1154623345 : if (var1.equals("CGLIB$findMethodProxy" )) { switch (var2.length) { case 1 : if (var2[0 ].getName().equals("net.sf.cglib.core.Signature" )) { return 14 ; } } } break ; case 1543336189 : if (var1.equals("CGLIB$toString$2" )) { switch (var2.length) { case 0 : return 20 ; } } break ; case 1811874389 : if (var1.equals("newInstance" )) { switch (var2.length) { case 1 : String var10001 = var2[0 ].getName(); switch (var10001.hashCode()) { case -845341380 : if (var10001.equals("net.sf.cglib.proxy.Callback" )) { return 5 ; } break ; case 1730110032 : if (var10001.equals("[Lnet.sf.cglib.proxy.Callback;" )) { return 4 ; } } case 2 : default : break ; case 3 : if (var2[0 ].getName().equals("[Ljava.lang.Class;" ) && var2[1 ].getName().equals("[Ljava.lang.Object;" ) && var2[2 ].getName().equals("[Lnet.sf.cglib.proxy.Callback;" )) { return 6 ; } } } break ; case 1817099975 : if (var1.equals("setCallback" )) { switch (var2.length) { case 2 : if (var2[0 ].getName().equals("int" ) && var2[1 ].getName().equals("net.sf.cglib.proxy.Callback" )) { return 8 ; } } } break ; case 1905679803 : if (var1.equals("getCallback" )) { switch (var2.length) { case 1 : if (var2[0 ].getName().equals("int" )) { return 12 ; } } } break ; case 1951977610 : if (var1.equals("CGLIB$clone$4" )) { switch (var2.length) { case 0 : return 17 ; } } } return -1 ; } public int getIndex (Class[] var1) { switch (var1.length) { case 0 : return 0 ; default : return -1 ; } } public Object invoke (int var1, Object var2, Object[] var3) throws InvocationTargetException { 53c7afed var10000 = (53c7afed)var2; int var10001 = var1; try { switch (var10001) { case 0 : return new Boolean(var10000.equals(var3[0 ])); case 1 : return var10000.toString(); case 2 : return new Integer(var10000.hashCode()); case 3 : return var10000.clone(); case 4 : return var10000.newInstance((Callback[])var3[0 ]); case 5 : return var10000.newInstance((Callback)var3[0 ]); case 6 : return var10000.newInstance((Class[])var3[0 ], (Object[])var3[1 ], (Callback[])var3[2 ]); case 7 : return var10000.sayHello((String)var3[0 ]); case 8 : var10000.setCallback(((Number)var3[0 ]).intValue(), (Callback)var3[1 ]); return null ; case 9 : 53c7afed.CGLIB$SET_STATIC_CALLBACKS((Callback[])var3[0 ]); return null ; case 10 : 53c7afed.CGLIB$SET_THREAD_CALLBACKS((Callback[])var3[0 ]); return null ; case 11 : return var10000.getCallbacks(); case 12 : return var10000.getCallback(((Number)var3[0 ]).intValue()); case 13 : var10000.setCallbacks((Callback[])var3[0 ]); return null ; case 14 : return 53c7afed.CGLIB$findMethodProxy((Signature)var3[0 ]); case 15 : 53c7afed.CGLIB$STATICHOOK1(); return null ; case 16 : return var10000.CGLIB$sayHello$0 ((String)var3[0 ]); case 17 : return var10000.CGLIB$clone$4 (); case 18 : return new Boolean(var10000.CGLIB$equals$1 (var3[0 ])); case 19 : return new Integer(var10000.CGLIB$hashCode$3 ()); case 20 : return var10000.CGLIB$toString$2 (); } } catch (Throwable var4) { throw new InvocationTargetException(var4); } throw new IllegalArgumentException("Cannot find matching method/constructor" ); } public Object newInstance (int var1, Object[] var2) throws InvocationTargetException { 53c7afed var10000 = new 53c7afed; 53c7afed var10001 = var10000; int var10002 = var1; try { switch (var10002) { case 0 : var10001.<init>(); return var10000; } } catch (Throwable var3) { throw new InvocationTargetException(var3); } throw new IllegalArgumentException("Cannot find matching method/constructor" ); } public int getMaxIndex () { return 20 ; } }
这个类更加长,但是其实大部分都是switch-case
的代码块,它的功能就是为方法的调用添加基于整型数字的索引,主要目的是为了减少反射调用的时间,将反射调用转化为直接调用。简单来说,invokeSuper的流程就是这样的 :
通过MethodProxy的init方法,用当前方法的Signature(签名)构建两个FastClass实例(当然,这里会做缓存)和当前方法对应的index,存放在FastClassInfo实例中。
通过FastClassInfo中的FastClass实例和index,在FastClass中找到对应的方法(在switch-case
块中基于整数索引index进行查找)直接调用。
直观来看,CGLIB在类生成期间的操作会相对耗时,而且生成的类数目比较多,会占据大量永久代或者元空间的内存。子类一旦生成,后面的方法调用就会变成搜索方法索引和直接调用,这样的操作在特定的条件下效率会比JDK的反射高。这里特定的场景是指CGLIB子类中的switch-case
块不大并且当前调用的方法的index在switch-case块的前部而不是中后部(简单来说就是子类中的方法要尽量少从而提高switch-case中的搜寻效率 )。详细可以参考这篇性能对比的文章:cglib和jdk动态代理调用性能测
小结 CGLIB提供了许多基于代码生成的高级功能的API,可以在通过上面的例子熟悉它的使用,并且在合适的场景用于实战中。可能最常用到的是基于Enhancer的动态代理,这里总结一下CGLIB和JDK动态代理的区别(老生常谈):
JDK动态代理只能够对接口进行代理,不能对普通的类进行代理(因为所有生成的代理类的父类为Proxy,Java类继承机制不允许多重继承);CGLIB能够代理普通类,但是该普通类必须能够被继承(不能用final修饰符)。
JDK动态代理使用Java原生的反射API进行操作,在生成类上比较高效;CGLIB使用ASM框架直接对字节码进行修改,使用了FastClass的特性,在某些情况下类的方法执行会比较高效。
(本文完 e-a-20181216 c-1-d)