@boothsun
2018-04-15T02:29:33.000000Z
字数 22834
阅读 1494
动态代理
内容大多来自于,以下优秀博文:
Java之美[从菜鸟到高手演练]之JDK动态代理的实现及原理
细说JDK动态代理的实现原理
为什么Java中类与类之间只能单继承。但是接口之间可以多继承?:
类与类之间的多继承可能会出现错误,如:一个类继承了两个父类,而这两个父类里面都有show()方法。
class Parent1 {public void show() {System.out.println("父类1");}}class Parent2{public void show(){System.out.println("父类2");}}class Zilei extends Parent1,Parent2{public static void main(String[] args){Zilei p=new Zilei();//此时调用出错,因为调用不确定,java代码不知道调用哪个父类的方法了。p.show();}}
此时,当子类需要调用父类的show()方法时,就会产生混淆,不知道这个show()方法是要调用哪个父类的show()方法。
但是接口是可以多继承的,这个是因为接口的方法并没有具体实现,只是一种定义规范,具体实现需要子类重写接口的方法,所以不存在如上调用的不确定性。
关于JDK的动态代理,最为人熟知的可能要数Spring AOP的实现,默认情况下,Spring AOP对于实现接口的类使用JDK动态代理,而对于没有实现任何接口的类则使用CGLIB来实现。那么,什么是JDK的动态代理呢?
JDK的动态代理,就是在程序运行的过程中,根据被代理的接口来动态生成代理类的class文件,并加载运行的过程。JDK1.3开始支持动态代理。那么JDK是如何生成动态代理的呢?JDK动态代理为什么不支持类的代理,只支持接口的代理?
首先来看一下如何使用JDK动态代理。JDK提供了java.lang.reflect.Proxy类来实现动态代理的,可通过它的newProxyInstance来获得被代理类的代理实现。同时对于代理的接口的实际处理,是一个java.lang.reflect.InvocationHandler,它提供了一个invoke方法供实现者提供相应的代理逻辑的实现。可以对实际的实现进行一些特殊的处理,像Spring AOP中的各种advice。下面来看看如何使用。
下面是个具体的案例:
public interface Hello {void say(String name);}public class HelloImpl implements Hello {@Overridepublic void say(String name) {System.out.println("Hello! " + name);}}// 代理工具类import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class DynamicProxy implements InvocationHandler {private Object target;public DynamicProxy(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// beforeSystem.out.println("before");method.invoke(target,args) ;System.out.println("after");return null;}public <T> T getProxy() {return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);}}public class RunMethod {public static void main(String[] args) {DynamicProxy dynamicProxy = new DynamicProxy(new HelloImpl());Hello helloProxy = dynamicProxy.getProxy() ;helloProxy.say("Jack");}}
运行结果如下:
beforeHello! Jackafter
从上面可以看出,JDK的动态代理使用起来非常简单,但是只知道如何使用是不够的,知其然,还需知其所以然,所以要搞清楚它的实现,还得从源码入手。首先,我们的入口便是上面测试类里的getProxy()方法,我们跟进去,看看这个方法:
public <T> T getProxy() {return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);}
也就是说,JDK的动态代理,是通过一个叫Proxy的类来实现的,我们继续跟进去,看看Proxy类的newProxyInstance()方法。
/*** Returns an instance of a proxy class for the specified interfaces* that dispatches method invocations to the specified invocation* handler.*/@CallerSensitivepublic static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) {Class<?> cl = getProxyClass0(loader, intfs);// ...final Constructor<?> cons = cl.getConstructor(constructorParams);// ...return cons.newInstance(new Object[]{h});}
首先,根据JDK的注释我们可知,newProxyInstance方法最终将返回一个实现了指定接口的类的实例对象。其三个参数分别是:ClasssLoader,指定的接口及我们自己定义的InvocationHandler类。
上面newProxyInstance方法体内的代码,是我们在分析JDK动态代理实现原理时,最需要关注的点。首先,我们通过getProxyClass0来获取代理类的Class对象,然后通过cl.getConstructor(constructorParams)拿到这个代理类的特定构造方法。最后一步,通过cons.newInstance(new Object[]{h})返回这个新的代理类的一个实例,注意:调用newInstance的时候,传入的参数为h,即我们自己定义好的InvocationHandler类,先记着这一步,后面我们就知道这里这样做的原因。
其实这三条代码,核心就是这个getProxyClass0方法,另外两行代码是Java反射的应用,和我们当前的兴趣点没什么关系,所以我们继续研究这个getProxyClass0方法。这个方法,注释很简单,如下:
/** Look up or generate the designated proxy class.*/Class<?> cl = getProxyClass0(loader, intfs);private static Class<?> getProxyClass0(ClassLoader loader,Class<?>... interfaces) {if (interfaces.length > 65535) {throw new IllegalArgumentException("interface limit exceeded");}// JDK对代理进行了缓存,如果已经存在相应的代理类,则直接返回,否则才会通过ProxyClassFactory来创建代理return proxyClassCache.get(loader, interfaces);}
这里用到了缓存,先从缓存里查一下,如果存在,直接返回,如果不存在就新创建。具体的缓存逻辑这里暂不关心,我们只关注代理类Class是如何生成的。在这个get方法里,我们看到了如下代码:
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
这里调用了apply()方法,它是Proxy类的内部类ProxyClassFactory实现其接口的一个方法,具体实现如下:
private static final class ProxyClassFactoryimplements BiFunction<ClassLoader, Class<?>[], Class<?>> {// 所有代理类名字的前缀private static final String proxyClassNamePrefix = "$Proxy";// 用于生成代理类名字的计数器private static final AtomicLong nextUniqueNumber = new AtomicLong();@Overridepublic Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {// 省略验证代理接口的代码……String proxyPkg = null; // 生成的代理类的包名// 对于非公共接口,代理类的包名与接口的相同for (Class<?> intf : interfaces) {int flags = intf.getModifiers();if (!Modifier.isPublic(flags)) {String name = intf.getName();int n = name.lastIndexOf('.');String pkg = ((n == -1) ? "" : name.substring(0, n + 1));if (proxyPkg == null) {proxyPkg = pkg;} else if (!pkg.equals(proxyPkg)) {throw new IllegalArgumentException("non-public interfaces from different packages");}}}// 对于公共接口的包名,默认为com.sun.proxyif (proxyPkg == null) {proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";}// 获取计数long num = nextUniqueNumber.getAndIncrement();// 默认情况下,代理类的完全限定名为:com.sun.proxy.$Proxy0,com.sun.proxy.$Proxy1……依次递增String proxyName = proxyPkg + proxyClassNamePrefix + num;// 这里才是真正的生成代理类的字节码的地方byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);try {// 根据二进制字节码返回相应的Class实例return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);} catch (ClassFormatError e) {throw new IllegalArgumentException(e.toString());}}}
上面的代码很长,我们重点关注真正的生成代理类的字节码的地方:
// 这里才是真正的生成代理类的字节码的地方byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);try {// 根据二进制字节码返回相应的Class实例return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);} catch (ClassFormatError e) {throw new IllegalArgumentException(e.toString());}
ProxyGenerator是sun.misc包中的类,它没有开源,但是可以反编译来一探究竟:
public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);final byte[] var4 = var3.generateClassFile();// 这里根据参数配置,决定是否把生成的字节码(.class文件)保存到本地磁盘,我们可以通过把相应的class文件保存到本地,再反编译来看看具体的实现,这样更直观if(saveGeneratedFiles) {AccessController.doPrivileged(new PrivilegedAction() {public Void run() {try {int var1 = var0.lastIndexOf(46);Path var2;if(var1 > 0) {Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar), new String[0]);Files.createDirectories(var3, new FileAttribute[0]);var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");} else {var2 = Paths.get(var0 + ".class", new String[0]);}Files.write(var2, var4, new OpenOption[0]);return null;} catch (IOException var4x) {throw new InternalError("I/O exception saving generated file: " + var4x);}}});}return var4;}
saveGeneratedFiles这个属性的值从哪里来呢:
private static final boolean saveGeneratedFiles = ((Boolean)AccessController.doPrivileged(new GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"))).booleanValue();
GetBooleanAction实际上是调用Boolean.getBoolean(propName)来获得的,而Boolean.getBoolean(propName)调用了System.getProperty(name),所以我们可以设置sun.misc.ProxyGenerator.saveGeneratedFiles这个系统属性为true来把生成的class保存到本地文件来查看。
反编译$Proxy0.class文件,如下所示:
public final class $Proxy0 extends Proxy implements Hello {private static Method m1;private static Method m2;private static Method m3;private static Method m0;public $Proxy0(InvocationHandler var1) throws {super(var1);}public final boolean equals(Object var1) throws {try {return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();} catch (RuntimeException | Error var3) {throw var3;} catch (Throwable var4) {throw new UndeclaredThrowableException(var4);}}public final String toString() throws {try {return (String)super.h.invoke(this, m2, (Object[])null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}public final void say(String var1) throws {try {super.h.invoke(this, m3, new Object[]{var1});} catch (RuntimeException | Error var3) {throw var3;} catch (Throwable var4) {throw new UndeclaredThrowableException(var4);}}public final int hashCode() throws {try {return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}static {try {m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);m3 = Class.forName("com.boothsun.zk.dynamic.Hello").getMethod("say", new Class[]{Class.forName("java.lang.String")});m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);} catch (NoSuchMethodException var2) {throw new NoSuchMethodError(var2.getMessage());} catch (ClassNotFoundException var3) {throw new NoClassDefFoundError(var3.getMessage());}}}
可以看到,动态生成的代理类有如下特性:
DynamicProxy类,从而让生成的代理类可以调用我们重写的“invoke”方法。至此JDK动态代理的实现原理就分析的差不多了。同时我们可以想像一下Spring AOP提供的各种拦截该如何实现,就已经很明了了,如下所示:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// BeforeAdviceObject retVal = null;try {// AroundAdviceretVal = method.invoke(target, args);// AroundAdvice// AfterReturningAdvice}catch (Throwable e) {// AfterThrowingAdvice}finally {// AfterAdvice}return retVal;}
上面是对于Spring AOP使用JDK动态代理实现的基本框架代码,当然具体的实现肯定比这个复杂得多,但是基本原理不外乎如是。所以理解基本原理对于理解其他的代码也是很有好处的。
JDK动态代理的原理是根据定义好的规则,用传入的接口创建一个新类,然后对接口中方法的调用都将转发到InvocationHandler中的Invoke方法上,这就是为什么采用动态代理时为什么只能用接口引用指向代理,而不能用传入的类引用执行动态类。
试想如果JDK中允许对实现类做代理,则获取的代理实例是必须强制转换为实现类,因为需要调用实现类中的方法,但是这种强转是会抛出类强制转换异常,因为代理实例和实现类确实不是同一种类型。但是强转为接口是可以的,向上转型。
JDK动态代理 = 反射 + 多态 + 动态生成Class技术。
马士兵:多态 = 在执行期间(非编译期)判断所引用对象实际类型,根据其实的类型调用其相应的方法。
// Hello HelloImpl 同上。// 获取代理类对象public class CGLibProxy implements MethodInterceptor {public <T> T getProxy(Class<T> cls) {return (T) Enhancer.create(cls, this);}@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {// beforeSystem.out.println("before");Object result = proxy.invokeSuper(obj, args);System.out.println("after");return result;}}// 单测public class CGLIBProxyTest {public static void main(String[] args) {System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "F:\\code");CGLibProxy cgLibProxy = new CGLibProxy();HelloImpl helloProxy = cgLibProxy.getProxy(HelloImpl.class);helloProxy.say("Jack");}}
运行结果:
CGLIB debugging enabled, writing to 'F:\code'beforeHello! Jackafter
JDK动态代理要求被代理的类必须实现接口,有很强的局限性。而CGLIB动态代理则没有此类强制限制要求。简单的说,CGLIB会让生成的代理类继承被代理类,当对被代理类调用时会转换为调用MethodInterceptor具体实现类的intercept方法。然后我们就可以做到:前置处理 ——> 调用被代理方法 ——> 后置处理。在CGLIB底层,其实是借助于ASM这个非常强大的Java字节码生成器。
这里不关注具体字节码生成技术,只关注代理的实现过程。
我们可以开发Debug模式,让CGLIB将生成的动态字节码保存在本地,然后使用jd-gui进行反编译。开启Debug模式:
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "F:\\code");
反编译后的代码:
// 1public class HelloImpl$$EnhancerByCGLIB$$7b44e866 extends HelloImplimplements Factory{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$say$0$Method;private static final MethodProxy CGLIB$say$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 localClass1 = Class.forName("com.boothsun.zk.dynamic.HelloImpl$$EnhancerByCGLIB$$7b44e866");Class localClass2;Method[] tmp83_80 = ReflectUtils.findMethods(new String[] { "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;" }, (localClass2 = Class.forName("java.lang.Object")).getDeclaredMethods());CGLIB$equals$1$Method = tmp83_80[0];CGLIB$equals$1$Proxy = MethodProxy.create(localClass2, localClass1, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");Method[] tmp103_83 = tmp83_80;CGLIB$toString$2$Method = tmp103_83[1];CGLIB$toString$2$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");Method[] tmp123_103 = tmp103_83;CGLIB$hashCode$3$Method = tmp123_103[2];CGLIB$hashCode$3$Proxy = MethodProxy.create(localClass2, localClass1, "()I", "hashCode", "CGLIB$hashCode$3");Method[] tmp143_123 = tmp123_103;CGLIB$clone$4$Method = tmp143_123[3];CGLIB$clone$4$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");tmp143_123;Method[] tmp191_188 = ReflectUtils.findMethods(new String[] { "say", "(Ljava/lang/String;)V" }, (localClass2 = Class.forName("com.boothsun.zk.dynamic.HelloImpl")).getDeclaredMethods());CGLIB$say$0$Method = tmp191_188[0];CGLIB$say$0$Proxy = MethodProxy.create(localClass2, localClass1, "(Ljava/lang/String;)V", "say", "CGLIB$say$0");tmp191_188;}final void CGLIB$say$0(String paramString){super.say(paramString);}public final void say(String paramString){MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;if (tmp4_1 == null){tmp4_1;CGLIB$BIND_CALLBACKS(this);}if (this.CGLIB$CALLBACK_0 != null)return;super.say(paramString);}final boolean CGLIB$equals$1(Object paramObject){return super.equals(paramObject);}public final boolean equals(Object paramObject){MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;if (tmp4_1 == null){tmp4_1;CGLIB$BIND_CALLBACKS(this);}MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;if (tmp17_14 != null){Object tmp41_36 = tmp17_14.intercept(this, CGLIB$equals$1$Method, new Object[] { paramObject }, CGLIB$equals$1$Proxy);tmp41_36;return tmp41_36 == null ? false : ((Boolean)tmp41_36).booleanValue();}return super.equals(paramObject);}final String CGLIB$toString$2(){return super.toString();}public final String toString(){MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;if (tmp4_1 == null){tmp4_1;CGLIB$BIND_CALLBACKS(this);}MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;if (tmp17_14 != null)return (String)tmp17_14.intercept(this, CGLIB$toString$2$Method, CGLIB$emptyArgs, CGLIB$toString$2$Proxy);return super.toString();}final int CGLIB$hashCode$3(){return super.hashCode();}public final int hashCode(){MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;if (tmp4_1 == null){tmp4_1;CGLIB$BIND_CALLBACKS(this);}MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;if (tmp17_14 != null){Object tmp36_31 = tmp17_14.intercept(this, CGLIB$hashCode$3$Method, CGLIB$emptyArgs, CGLIB$hashCode$3$Proxy);tmp36_31;return tmp36_31 == null ? 0 : ((Number)tmp36_31).intValue();}return super.hashCode();}final Object CGLIB$clone$4()throws CloneNotSupportedException{return super.clone();}protected final Object clone()throws CloneNotSupportedException{MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;if (tmp4_1 == null){tmp4_1;CGLIB$BIND_CALLBACKS(this);}MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;if (tmp17_14 != null)return tmp17_14.intercept(this, CGLIB$clone$4$Method, CGLIB$emptyArgs, CGLIB$clone$4$Proxy);return super.clone();}public static MethodProxy CGLIB$findMethodProxy(Signature paramSignature){String tmp4_1 = paramSignature.toString();switch (tmp4_1.hashCode()){case -1853413644:if (tmp4_1.equals("say(Ljava/lang/String;)V"))return CGLIB$say$0$Proxy;break;case -508378822:case 1826985398:case 1913648695:case 1984935277:}}public HelloImpl$$EnhancerByCGLIB$$7b44e866(){CGLIB$BIND_CALLBACKS(this);}public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] paramArrayOfCallback){CGLIB$THREAD_CALLBACKS.set(paramArrayOfCallback);}public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] paramArrayOfCallback){CGLIB$STATIC_CALLBACKS = paramArrayOfCallback;}private static final void CGLIB$BIND_CALLBACKS(Object paramObject){// Byte code:// 0: aload_0// 1: checkcast 2 com/boothsun/zk/dynamic/HelloImpl$$EnhancerByCGLIB$$7b44e866// 4: astore_1// 5: aload_1// 6: getfield 188 com/boothsun/zk/dynamic/HelloImpl$$EnhancerByCGLIB$$7b44e866:CGLIB$BOUND Z// 9: ifne +43 -> 52// 12: aload_1// 13: iconst_1// 14: putfield 188 com/boothsun/zk/dynamic/HelloImpl$$EnhancerByCGLIB$$7b44e866:CGLIB$BOUND Z// 17: getstatic 27 com/boothsun/zk/dynamic/HelloImpl$$EnhancerByCGLIB$$7b44e866:CGLIB$THREAD_CALLBACKS Ljava/lang/ThreadLocal;// 20: invokevirtual 191 java/lang/ThreadLocal:get ()Ljava/lang/Object;// 23: dup// 24: ifnonnull +15 -> 39// 27: pop// 28: getstatic 186 com/boothsun/zk/dynamic/HelloImpl$$EnhancerByCGLIB$$7b44e866:CGLIB$STATIC_CALLBACKS [Lorg/springframework/cglib/proxy/Callback;// 31: dup// 32: ifnonnull +7 -> 39// 35: pop// 36: goto +16 -> 52// 39: checkcast 192 [Lorg/springframework/cglib/proxy/Callback;// 42: aload_1// 43: swap// 44: iconst_0// 45: aaload// 46: checkcast 52 org/springframework/cglib/proxy/MethodInterceptor// 49: putfield 40 com/boothsun/zk/dynamic/HelloImpl$$EnhancerByCGLIB$$7b44e866:CGLIB$CALLBACK_0 Lorg/springframework/cglib/proxy/MethodInterceptor;// 52: return}public Object newInstance(Callback[] paramArrayOfCallback){CGLIB$SET_THREAD_CALLBACKS(paramArrayOfCallback);CGLIB$SET_THREAD_CALLBACKS(null);return new 7b44e866();}public Object newInstance(Callback paramCallback){CGLIB$SET_THREAD_CALLBACKS(new Callback[] { paramCallback });CGLIB$SET_THREAD_CALLBACKS(null);return new 7b44e866();}public Object newInstance(Class[] paramArrayOfClass, Object[] paramArrayOfObject, Callback[] paramArrayOfCallback){CGLIB$SET_THREAD_CALLBACKS(paramArrayOfCallback);Class[] tmp9_8 = paramArrayOfClass;switch (tmp9_8.length){case 0:tmp9_8;break;default:new 7b44e866();throw new IllegalArgumentException("Constructor not found");}CGLIB$SET_THREAD_CALLBACKS(null);}public Callback getCallback(int paramInt){CGLIB$BIND_CALLBACKS(this);switch (paramInt){case 0:break;}return null;}public void setCallback(int paramInt, Callback paramCallback){switch (paramInt){case 0:this.CGLIB$CALLBACK_0 = ((MethodInterceptor)paramCallback);break;}}public Callback[] getCallbacks(){CGLIB$BIND_CALLBACKS(this);return new Callback[] { this.CGLIB$CALLBACK_0 };}public void setCallbacks(Callback[] paramArrayOfCallback){this.CGLIB$CALLBACK_0 = ((MethodInterceptor)paramArrayOfCallback[0]);}static{CGLIB$STATICHOOK1();}}// 2public class HelloImpl$$FastClassByCGLIB$$767b95f1 extends FastClass{public HelloImpl$$FastClassByCGLIB$$767b95f1(Class paramClass){super(paramClass);}public int getIndex(Signature paramSignature){String tmp4_1 = paramSignature.toString();switch (tmp4_1.hashCode()){case -1853413644:if (tmp4_1.equals("say(Ljava/lang/String;)V"))return 0;break;case 1826985398:case 1913648695:case 1984935277:}}public int getIndex(String paramString, Class[] paramArrayOfClass){String tmp3_0 = paramString;switch (tmp3_0.hashCode()){case -1776922004:if (tmp3_0.equals("toString")){Class[] tmp56_1 = paramArrayOfClass;switch (tmp56_1.length){case 0:tmp56_1;return 2;}}break;case -1295482945:case 113643:case 147696667:}}public int getIndex(Class[] paramArrayOfClass){Class[] tmp1_0 = paramArrayOfClass;switch (tmp1_0.length){case 0:tmp1_0;return 0;}break label26;}// ERROR //public Object invoke(int paramInt, Object paramObject, Object[] paramArrayOfObject)throws InvocationTargetException{// Byte code:// 0: aload_2// 1: checkcast 61 com/boothsun/zk/dynamic/HelloImpl// 4: iload_1// 5: tableswitch default:+73 -> 78, 0:+31->36, 1:+42->47, 2:+57->62, 3:+61->66// 37: iconst_0// 38: aaload// 39: checkcast 37 java/lang/String// 42: invokevirtual 64 com/boothsun/zk/dynamic/HelloImpl:say (Ljava/lang/String;)V// 45: aconst_null// 46: areturn// 47: aload_3// 48: iconst_0// 49: aaload// 50: invokevirtual 65 com/boothsun/zk/dynamic/HelloImpl:equals (Ljava/lang/Object;)Z// 53: new 67 java/lang/Boolean// 56: dup_x1// 57: swap// 58: invokespecial 70 java/lang/Boolean:<init> (Z)V// 61: areturn// 62: invokevirtual 71 com/boothsun/zk/dynamic/HelloImpl:toString ()Ljava/lang/String;// 65: areturn// 66: invokevirtual 72 com/boothsun/zk/dynamic/HelloImpl:hashCode ()I// 69: new 74 java/lang/Integer// 72: dup_x1// 73: swap// 74: invokespecial 77 java/lang/Integer:<init> (I)V// 77: areturn// 78: goto +12 -> 90// 81: new 59 java/lang/reflect/InvocationTargetException// 84: dup_x1// 85: swap// 86: invokespecial 82 java/lang/reflect/InvocationTargetException:<init> (Ljava/lang/Throwable;)V// 89: athrow// 90: new 84 java/lang/IllegalArgumentException// 93: dup// 94: ldc 86// 96: invokespecial 88 java/lang/IllegalArgumentException:<init> (Ljava/lang/String;)V// 99: athrow//// Exception table:// from to target type// 5 81 81 java/lang/Throwable}public Object newInstance(int paramInt, Object[] paramArrayOfObject)throws InvocationTargetException{try{switch (paramInt){case 0:return new HelloImpl();}}catch (Throwable localThrowable){throw new InvocationTargetException(localThrowable);}throw new IllegalArgumentException("Cannot find matching method/constructor");}public int getMaxIndex(){return 3;}}
首先,我们关注HelloImpl$$EnhancerByCGLIB$$7b44e866这个类。
public class HelloImpl$$EnhancerByCGLIB$$7b44e866 extends HelloImplimplements Factory
从上面的代码我们知道,CGLIB生成的动态代理类是继承自被代理类,然后重写了里面的方法,并添加了新的方法。在这里我们需要注意一点:如果被代理类被final修改,那么它不可被继承,即不可被代理;同样,如果委托类中存在final修饰的方法,那么该方法也不可被代理。
final void CGLIB$say$0(String paramString) {super.say(paramString);}public final void say(String paramString) {MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;if (tmp4_1 == null) {tmp4_1;CGLIB$BIND_CALLBACKS(this);}if (this.CGLIB$CALLBACK_0 != null)return;super.say(paramString);}
代理类会为被代理的方法生成两个相关的方法,一个是重写的say方法,另一个是CGLIB$say$0方法,我们可以看到后者是直接调用父类中的同样方法。下面,我们重点分析say方法,看看是怎么实现代理功能的。
当调用代理类的say方法时,会先判断是否已经存在了MethodInterceptor接口的拦截器,如果没有的话就调用CGLIB$BIND_CALLBACKS(this);方法来获取Interceptor对象,CGLIB$BIND_CALLBACKS的反编译结果如下:
private static final void CGLIB$BIND_CALLBACKS(java.lang.Object);Code:0: aload_01: checkcast #2; //class net/sf/cglib/test/Target$$EnhancerByCGLIB$$788444a04: astore_15: aload_16: getfield #212; //Field CGLIB$BOUND:Z9: ifne 5212: aload_113: iconst_114: putfield #212; //Field CGLIB$BOUND:Z17: getstatic #24; //Field CGLIB$THREAD_CALLBACKS:Ljava/lang/ThreadLocal;20: invokevirtual #215; //Method java/lang/ThreadLocal.get:()Ljava/lang/Object;23: dup24: ifnonnull 3927: pop28: getstatic #210; //Field CGLIB$STATIC_CALLBACKS:[Lnet/sf/cglib/proxy/Callback;31: dup32: ifnonnull 3935: pop36: goto 5239: checkcast #216; //class "[Lnet/sf/cglib/proxy/Callback;"42: aload_143: swap44: iconst_045: aaload46: checkcast #48; //class net/sf/cglib/proxy/MethodInterceptor49: putfield #36; //Field CGLIB$CALLBACK_0:Lnet/sf/cglib/proxy/MethodInterceptor;52: return
为了方便阅读,等价的代码如下:
private static final void CGLIB$BIND_CALLBACKS(Object o){Target$$EnhancerByCGLIB$$788444a0 temp_1 = (Target$$EnhancerByCGLIB$$788444a0)o;Object temp_2;Callback[] temp_3if(temp_1.CGLIB$BOUND == true){return;}temp_1.CGLIB$BOUND = true;temp_2 = CGLIB$THREAD_CALLBACKS.get();if(temp_2!=null){temp_3 = (Callback[])temp_2;}else if(CGLIB$STATIC_CALLBACKS!=null){temp_3 = CGLIB$STATIC_CALLBACKS;}else{return;}temp_1.CGLIB$CALLBACK_0 = (MethodInterceptor)temp_3[0];return;}
CGLIB$BIND_CALLBACKS先从CGLIB$THREAD_CALLBACKS中get拦截对象,如果获取不到的话,再从CGLIB$STATIC_CALLBACKS来获取,如果也没有则认为该方法不需要代理。
那么拦截对象是如何设置到CGLIB$THREAD_CALLBACKS 或者 CGLIB$STATIC_CALLBACKS中的呢?
在Jdk动态代理中拦截类是在实例化代理类时由构造函数传入的,在cglib中是调用Enhancer的firstInstance方法来生成代理类实例并设置拦截对象的
1. 后面内容未完,待MS结束后继续往下写。 写时可参考文章:
静态代理与JDK动态代理、CGLIB动态代理 三者之间区别联系:
| 代理方式 | 实现 | 优点 | 缺点 | 特点 |
|---|---|---|---|---|
| JDK静态代理 | 代理类与委托类实现同一接口,并且在代理类中需要硬编码接口 | 实现简单,容易理解 | 代理类需要硬编码接口,在实际应用中可能会导致重复编码,浪费存储空间并且效率很低 | 好像没啥特点 |
| JDK动态代理 | 代理类与委托类实现同一接口,主要是通过代理类实现InvocationHandler并重写invoke方法来进行动态代理的,在invoke方法中将对方法进行增强处理 | 不需要硬编码接口,代码复用率高 | 只能够代理实现了接口的委托类 | 底层使用反射机制进行方法的调用 |
| CGLIB动态代理 | 代理类将委托类作为自己的父类并为其中的非final委托方法创建两个方法,一个是与委托方法签名相同的方法,它在方法中会通过super调用委托方法;另一个是代理类独有的方法。在代理方法中,它会判断是否存在实现了MethodInterceptor接口的对象,若存在则将调用intercept方法对委托方法进行代理 | 可以在运行时对类或者是接口进行增强操作,且委托类无需实现接口 | 不能对final类以及final方法进行代理 | 底层将方法全部存入一个数组中,通过数组索引直接进行方法调用 |