[关闭]
@boothsun 2018-04-15T10:29:33.000000Z 字数 22834 阅读 1275

JDK动态代理实现原理(未完 待MS后 继续往下写)

动态代理


内容大多来自于,以下优秀博文:
Java之美[从菜鸟到高手演练]之JDK动态代理的实现及原理
细说JDK动态代理的实现原理

插播一个小的简单知识点

为什么Java中类与类之间只能单继承。但是接口之间可以多继承?:

类与类之间的多继承可能会出现错误,如:一个类继承了两个父类,而这两个父类里面都有show()方法。

  1. class Parent1 {
  2. public void show() {
  3. System.out.println("父类1");
  4.   }
  5. }
  6. class Parent2{
  7. public void show(){
  8. System.out.println("父类2");
  9. }
  10. }
  11. class Zilei extends Parent1,Parent2{
  12.     public static void main(String[] args){
  13.       Zilei p=new Zilei();
  14.       //此时调用出错,因为调用不确定,java代码不知道调用哪个父类的方法了。
  15.       p.show();
  16.    }
  17. }

此时,当子类需要调用父类的show()方法时,就会产生混淆,不知道这个show()方法是要调用哪个父类的show()方法。

但是接口是可以多继承的,这个是因为接口的方法并没有具体实现,只是一种定义规范,具体实现需要子类重写接口的方法,所以不存在如上调用的不确定性。

JDK动态代理实现原理

JDK动态代理的使用

关于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。下面来看看如何使用。

下面是个具体的案例:

  1. public interface Hello {
  2. void say(String name);
  3. }
  4. public class HelloImpl implements Hello {
  5. @Override
  6. public void say(String name) {
  7. System.out.println("Hello! " + name);
  8. }
  9. }
  10. // 代理工具类
  11. import java.lang.reflect.InvocationHandler;
  12. import java.lang.reflect.Method;
  13. import java.lang.reflect.Proxy;
  14. public class DynamicProxy implements InvocationHandler {
  15. private Object target;
  16. public DynamicProxy(Object target) {
  17. this.target = target;
  18. }
  19. @Override
  20. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  21. // before
  22. System.out.println("before");
  23. method.invoke(target,args) ;
  24. System.out.println("after");
  25. return null;
  26. }
  27. public <T> T getProxy() {
  28. return (T) Proxy.newProxyInstance(
  29. target.getClass().getClassLoader(),
  30. target.getClass().getInterfaces(),
  31. this
  32. );
  33. }
  34. }
  35. public class RunMethod {
  36. public static void main(String[] args) {
  37. DynamicProxy dynamicProxy = new DynamicProxy(new HelloImpl());
  38. Hello helloProxy = dynamicProxy.getProxy() ;
  39. helloProxy.say("Jack");
  40. }
  41. }

运行结果如下:

  1. before
  2. Hello! Jack
  3. after

JDK动态代理的实现原理

从上面可以看出,JDK的动态代理使用起来非常简单,但是只知道如何使用是不够的,知其然,还需知其所以然,所以要搞清楚它的实现,还得从源码入手。首先,我们的入口便是上面测试类里的getProxy()方法,我们跟进去,看看这个方法:

  1. public <T> T getProxy() {
  2. return (T) Proxy.newProxyInstance(
  3. target.getClass().getClassLoader(),
  4. target.getClass().getInterfaces(),
  5. this
  6. );
  7. }

也就是说,JDK的动态代理,是通过一个叫Proxy的类来实现的,我们继续跟进去,看看Proxy类的newProxyInstance()方法。

  1. /**
  2. * Returns an instance of a proxy class for the specified interfaces
  3. * that dispatches method invocations to the specified invocation
  4. * handler.
  5. */
  6. @CallerSensitive
  7. public static Object newProxyInstance(ClassLoader loader,
  8. Class<?>[] interfaces,
  9. InvocationHandler h) {
  10. Class<?> cl = getProxyClass0(loader, intfs);
  11. // ...
  12. final Constructor<?> cons = cl.getConstructor(constructorParams);
  13. // ...
  14. return cons.newInstance(new Object[]{h});
  15. }

首先,根据JDK的注释我们可知,newProxyInstance方法最终将返回一个实现了指定接口的类的实例对象。其三个参数分别是:ClasssLoader,指定的接口及我们自己定义的InvocationHandler类。

上面newProxyInstance方法体内的代码,是我们在分析JDK动态代理实现原理时,最需要关注的点。首先,我们通过getProxyClass0来获取代理类的Class对象,然后通过cl.getConstructor(constructorParams)拿到这个代理类的特定构造方法。最后一步,通过cons.newInstance(new Object[]{h})返回这个新的代理类的一个实例,注意:调用newInstance的时候,传入的参数为h,即我们自己定义好的InvocationHandler类,先记着这一步,后面我们就知道这里这样做的原因。

其实这三条代码,核心就是这个getProxyClass0方法,另外两行代码是Java反射的应用,和我们当前的兴趣点没什么关系,所以我们继续研究这个getProxyClass0方法。这个方法,注释很简单,如下:

  1. /*
  2. * Look up or generate the designated proxy class.
  3. */
  4. Class<?> cl = getProxyClass0(loader, intfs);
  5. private static Class<?> getProxyClass0(ClassLoader loader,
  6. Class<?>... interfaces) {
  7. if (interfaces.length > 65535) {
  8. throw new IllegalArgumentException("interface limit exceeded");
  9. }
  10. // JDK对代理进行了缓存,如果已经存在相应的代理类,则直接返回,否则才会通过ProxyClassFactory来创建代理
  11. return proxyClassCache.get(loader, interfaces);
  12. }

这里用到了缓存,先从缓存里查一下,如果存在,直接返回,如果不存在就新创建。具体的缓存逻辑这里暂不关心,我们只关注代理类Class是如何生成的。在这个get方法里,我们看到了如下代码:

  1. Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));

这里调用了apply()方法,它是Proxy类的内部类ProxyClassFactory实现其接口的一个方法,具体实现如下:

  1. private static final class ProxyClassFactory
  2. implements BiFunction<ClassLoader, Class<?>[], Class<?>> {
  3. // 所有代理类名字的前缀
  4. private static final String proxyClassNamePrefix = "$Proxy";
  5. // 用于生成代理类名字的计数器
  6. private static final AtomicLong nextUniqueNumber = new AtomicLong();
  7. @Override
  8. public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
  9. // 省略验证代理接口的代码……
  10. String proxyPkg = null; // 生成的代理类的包名
  11. // 对于非公共接口,代理类的包名与接口的相同
  12. for (Class<?> intf : interfaces) {
  13. int flags = intf.getModifiers();
  14. if (!Modifier.isPublic(flags)) {
  15. String name = intf.getName();
  16. int n = name.lastIndexOf('.');
  17. String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
  18. if (proxyPkg == null) {
  19. proxyPkg = pkg;
  20. } else if (!pkg.equals(proxyPkg)) {
  21. throw new IllegalArgumentException(
  22. "non-public interfaces from different packages");
  23. }
  24. }
  25. }
  26. // 对于公共接口的包名,默认为com.sun.proxy
  27. if (proxyPkg == null) {
  28. proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
  29. }
  30. // 获取计数
  31. long num = nextUniqueNumber.getAndIncrement();
  32. // 默认情况下,代理类的完全限定名为:com.sun.proxy.$Proxy0,com.sun.proxy.$Proxy1……依次递增
  33. String proxyName = proxyPkg + proxyClassNamePrefix + num;
  34. // 这里才是真正的生成代理类的字节码的地方
  35. byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
  36. proxyName, interfaces);
  37. try {
  38. // 根据二进制字节码返回相应的Class实例
  39. return defineClass0(loader, proxyName,
  40. proxyClassFile, 0, proxyClassFile.length);
  41. } catch (ClassFormatError e) {
  42. throw new IllegalArgumentException(e.toString());
  43. }
  44. }
  45. }

上面的代码很长,我们重点关注真正的生成代理类的字节码的地方:

  1. // 这里才是真正的生成代理类的字节码的地方
  2. byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
  3. proxyName, interfaces);
  4. try {
  5. // 根据二进制字节码返回相应的Class实例
  6. return defineClass0(loader, proxyName,
  7. proxyClassFile, 0, proxyClassFile.length);
  8. } catch (ClassFormatError e) {
  9. throw new IllegalArgumentException(e.toString());
  10. }

ProxyGeneratorsun.misc包中的类,它没有开源,但是可以反编译来一探究竟:

  1. public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
  2. ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
  3. final byte[] var4 = var3.generateClassFile();
  4. // 这里根据参数配置,决定是否把生成的字节码(.class文件)保存到本地磁盘,我们可以通过把相应的class文件保存到本地,再反编译来看看具体的实现,这样更直观
  5. if(saveGeneratedFiles) {
  6. AccessController.doPrivileged(new PrivilegedAction() {
  7. public Void run() {
  8. try {
  9. int var1 = var0.lastIndexOf(46);
  10. Path var2;
  11. if(var1 > 0) {
  12. Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar), new String[0]);
  13. Files.createDirectories(var3, new FileAttribute[0]);
  14. var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
  15. } else {
  16. var2 = Paths.get(var0 + ".class", new String[0]);
  17. }
  18. Files.write(var2, var4, new OpenOption[0]);
  19. return null;
  20. } catch (IOException var4x) {
  21. throw new InternalError("I/O exception saving generated file: " + var4x);
  22. }
  23. }
  24. });
  25. }
  26. return var4;
  27. }

saveGeneratedFiles这个属性的值从哪里来呢:

  1. 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文件,如下所示:

  1. public final class $Proxy0 extends Proxy implements Hello {
  2. private static Method m1;
  3. private static Method m2;
  4. private static Method m3;
  5. private static Method m0;
  6. public $Proxy0(InvocationHandler var1) throws {
  7. super(var1);
  8. }
  9. public final boolean equals(Object var1) throws {
  10. try {
  11. return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
  12. } catch (RuntimeException | Error var3) {
  13. throw var3;
  14. } catch (Throwable var4) {
  15. throw new UndeclaredThrowableException(var4);
  16. }
  17. }
  18. public final String toString() throws {
  19. try {
  20. return (String)super.h.invoke(this, m2, (Object[])null);
  21. } catch (RuntimeException | Error var2) {
  22. throw var2;
  23. } catch (Throwable var3) {
  24. throw new UndeclaredThrowableException(var3);
  25. }
  26. }
  27. public final void say(String var1) throws {
  28. try {
  29. super.h.invoke(this, m3, new Object[]{var1});
  30. } catch (RuntimeException | Error var3) {
  31. throw var3;
  32. } catch (Throwable var4) {
  33. throw new UndeclaredThrowableException(var4);
  34. }
  35. }
  36. public final int hashCode() throws {
  37. try {
  38. return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
  39. } catch (RuntimeException | Error var2) {
  40. throw var2;
  41. } catch (Throwable var3) {
  42. throw new UndeclaredThrowableException(var3);
  43. }
  44. }
  45. static {
  46. try {
  47. m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
  48. m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
  49. m3 = Class.forName("com.boothsun.zk.dynamic.Hello").getMethod("say", new Class[]{Class.forName("java.lang.String")});
  50. m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
  51. } catch (NoSuchMethodException var2) {
  52. throw new NoSuchMethodError(var2.getMessage());
  53. } catch (ClassNotFoundException var3) {
  54. throw new NoClassDefFoundError(var3.getMessage());
  55. }
  56. }
  57. }

可以看到,动态生成的代理类有如下特性:

至此JDK动态代理的实现原理就分析的差不多了。同时我们可以想像一下Spring AOP提供的各种拦截该如何实现,就已经很明了了,如下所示:

  1. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  2. // BeforeAdvice
  3. Object retVal = null;
  4. try {
  5. // AroundAdvice
  6. retVal = method.invoke(target, args);
  7. // AroundAdvice
  8. // AfterReturningAdvice
  9. }
  10. catch (Throwable e) {
  11. // AfterThrowingAdvice
  12. }
  13. finally {
  14. // AfterAdvice
  15. }
  16. return retVal;
  17. }

上面是对于Spring AOP使用JDK动态代理实现的基本框架代码,当然具体的实现肯定比这个复杂得多,但是基本原理不外乎如是。所以理解基本原理对于理解其他的代码也是很有好处的。

总结

JDK动态代理的原理是根据定义好的规则,用传入的接口创建一个新类,然后对接口中方法的调用都将转发到InvocationHandler中的Invoke方法上,这就是为什么采用动态代理时为什么只能用接口引用指向代理,而不能用传入的类引用执行动态类。

试想如果JDK中允许对实现类做代理,则获取的代理实例是必须强制转换为实现类,因为需要调用实现类中的方法,但是这种强转是会抛出类强制转换异常,因为代理实例和实现类确实不是同一种类型。但是强转为接口是可以的,向上转型。

JDK动态代理 = 反射 + 多态 + 动态生成Class技术。

马士兵:多态 = 在执行期间(非编译期)判断所引用对象实际类型,根据其实的类型调用其相应的方法。

CGLIB 动态代理学习

使用示例

  1. // Hello HelloImpl 同上。
  2. // 获取代理类对象
  3. public class CGLibProxy implements MethodInterceptor {
  4. public <T> T getProxy(Class<T> cls) {
  5. return (T) Enhancer.create(cls, this);
  6. }
  7. @Override
  8. public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
  9. // before
  10. System.out.println("before");
  11. Object result = proxy.invokeSuper(obj, args);
  12. System.out.println("after");
  13. return result;
  14. }
  15. }
  16. // 单测
  17. public class CGLIBProxyTest {
  18. public static void main(String[] args) {
  19. System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "F:\\code");
  20. CGLibProxy cgLibProxy = new CGLibProxy();
  21. HelloImpl helloProxy = cgLibProxy.getProxy(HelloImpl.class);
  22. helloProxy.say("Jack");
  23. }
  24. }

运行结果:

  1. CGLIB debugging enabled, writing to 'F:\code'
  2. before
  3. Hello! Jack
  4. after

JDK动态代理要求被代理的类必须实现接口,有很强的局限性。而CGLIB动态代理则没有此类强制限制要求。简单的说,CGLIB会让生成的代理类继承被代理类,当对被代理类调用时会转换为调用MethodInterceptor具体实现类的intercept方法。然后我们就可以做到:前置处理 ——> 调用被代理方法 ——> 后置处理。在CGLIB底层,其实是借助于ASM这个非常强大的Java字节码生成器。

具体实现原理

这里不关注具体字节码生成技术,只关注代理的实现过程。

我们可以开发Debug模式,让CGLIB将生成的动态字节码保存在本地,然后使用jd-gui进行反编译。开启Debug模式:

  1. System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "F:\\code");

反编译后的代码:

  1. // 1
  2. public class HelloImpl$$EnhancerByCGLIB$$7b44e866 extends HelloImpl
  3. implements Factory
  4. {
  5. private boolean CGLIB$BOUND;
  6. public static Object CGLIB$FACTORY_DATA;
  7. private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
  8. private static final Callback[] CGLIB$STATIC_CALLBACKS;
  9. private MethodInterceptor CGLIB$CALLBACK_0;
  10. private static Object CGLIB$CALLBACK_FILTER;
  11. private static final Method CGLIB$say$0$Method;
  12. private static final MethodProxy CGLIB$say$0$Proxy;
  13. private static final Object[] CGLIB$emptyArgs;
  14. private static final Method CGLIB$equals$1$Method;
  15. private static final MethodProxy CGLIB$equals$1$Proxy;
  16. private static final Method CGLIB$toString$2$Method;
  17. private static final MethodProxy CGLIB$toString$2$Proxy;
  18. private static final Method CGLIB$hashCode$3$Method;
  19. private static final MethodProxy CGLIB$hashCode$3$Proxy;
  20. private static final Method CGLIB$clone$4$Method;
  21. private static final MethodProxy CGLIB$clone$4$Proxy;
  22. static void CGLIB$STATICHOOK1()
  23. {
  24. CGLIB$THREAD_CALLBACKS = new ThreadLocal();
  25. CGLIB$emptyArgs = new Object[0];
  26. Class localClass1 = Class.forName("com.boothsun.zk.dynamic.HelloImpl$$EnhancerByCGLIB$$7b44e866");
  27. Class localClass2;
  28. 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());
  29. CGLIB$equals$1$Method = tmp83_80[0];
  30. CGLIB$equals$1$Proxy = MethodProxy.create(localClass2, localClass1, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");
  31. Method[] tmp103_83 = tmp83_80;
  32. CGLIB$toString$2$Method = tmp103_83[1];
  33. CGLIB$toString$2$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");
  34. Method[] tmp123_103 = tmp103_83;
  35. CGLIB$hashCode$3$Method = tmp123_103[2];
  36. CGLIB$hashCode$3$Proxy = MethodProxy.create(localClass2, localClass1, "()I", "hashCode", "CGLIB$hashCode$3");
  37. Method[] tmp143_123 = tmp123_103;
  38. CGLIB$clone$4$Method = tmp143_123[3];
  39. CGLIB$clone$4$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");
  40. tmp143_123;
  41. Method[] tmp191_188 = ReflectUtils.findMethods(new String[] { "say", "(Ljava/lang/String;)V" }, (localClass2 = Class.forName("com.boothsun.zk.dynamic.HelloImpl")).getDeclaredMethods());
  42. CGLIB$say$0$Method = tmp191_188[0];
  43. CGLIB$say$0$Proxy = MethodProxy.create(localClass2, localClass1, "(Ljava/lang/String;)V", "say", "CGLIB$say$0");
  44. tmp191_188;
  45. }
  46. final void CGLIB$say$0(String paramString)
  47. {
  48. super.say(paramString);
  49. }
  50. public final void say(String paramString)
  51. {
  52. MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
  53. if (tmp4_1 == null)
  54. {
  55. tmp4_1;
  56. CGLIB$BIND_CALLBACKS(this);
  57. }
  58. if (this.CGLIB$CALLBACK_0 != null)
  59. return;
  60. super.say(paramString);
  61. }
  62. final boolean CGLIB$equals$1(Object paramObject)
  63. {
  64. return super.equals(paramObject);
  65. }
  66. public final boolean equals(Object paramObject)
  67. {
  68. MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
  69. if (tmp4_1 == null)
  70. {
  71. tmp4_1;
  72. CGLIB$BIND_CALLBACKS(this);
  73. }
  74. MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
  75. if (tmp17_14 != null)
  76. {
  77. Object tmp41_36 = tmp17_14.intercept(this, CGLIB$equals$1$Method, new Object[] { paramObject }, CGLIB$equals$1$Proxy);
  78. tmp41_36;
  79. return tmp41_36 == null ? false : ((Boolean)tmp41_36).booleanValue();
  80. }
  81. return super.equals(paramObject);
  82. }
  83. final String CGLIB$toString$2()
  84. {
  85. return super.toString();
  86. }
  87. public final String toString()
  88. {
  89. MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
  90. if (tmp4_1 == null)
  91. {
  92. tmp4_1;
  93. CGLIB$BIND_CALLBACKS(this);
  94. }
  95. MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
  96. if (tmp17_14 != null)
  97. return (String)tmp17_14.intercept(this, CGLIB$toString$2$Method, CGLIB$emptyArgs, CGLIB$toString$2$Proxy);
  98. return super.toString();
  99. }
  100. final int CGLIB$hashCode$3()
  101. {
  102. return super.hashCode();
  103. }
  104. public final int hashCode()
  105. {
  106. MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
  107. if (tmp4_1 == null)
  108. {
  109. tmp4_1;
  110. CGLIB$BIND_CALLBACKS(this);
  111. }
  112. MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
  113. if (tmp17_14 != null)
  114. {
  115. Object tmp36_31 = tmp17_14.intercept(this, CGLIB$hashCode$3$Method, CGLIB$emptyArgs, CGLIB$hashCode$3$Proxy);
  116. tmp36_31;
  117. return tmp36_31 == null ? 0 : ((Number)tmp36_31).intValue();
  118. }
  119. return super.hashCode();
  120. }
  121. final Object CGLIB$clone$4()
  122. throws CloneNotSupportedException
  123. {
  124. return super.clone();
  125. }
  126. protected final Object clone()
  127. throws CloneNotSupportedException
  128. {
  129. MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
  130. if (tmp4_1 == null)
  131. {
  132. tmp4_1;
  133. CGLIB$BIND_CALLBACKS(this);
  134. }
  135. MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
  136. if (tmp17_14 != null)
  137. return tmp17_14.intercept(this, CGLIB$clone$4$Method, CGLIB$emptyArgs, CGLIB$clone$4$Proxy);
  138. return super.clone();
  139. }
  140. public static MethodProxy CGLIB$findMethodProxy(Signature paramSignature)
  141. {
  142. String tmp4_1 = paramSignature.toString();
  143. switch (tmp4_1.hashCode())
  144. {
  145. case -1853413644:
  146. if (tmp4_1.equals("say(Ljava/lang/String;)V"))
  147. return CGLIB$say$0$Proxy;
  148. break;
  149. case -508378822:
  150. case 1826985398:
  151. case 1913648695:
  152. case 1984935277:
  153. }
  154. }
  155. public HelloImpl$$EnhancerByCGLIB$$7b44e866()
  156. {
  157. CGLIB$BIND_CALLBACKS(this);
  158. }
  159. public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] paramArrayOfCallback)
  160. {
  161. CGLIB$THREAD_CALLBACKS.set(paramArrayOfCallback);
  162. }
  163. public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] paramArrayOfCallback)
  164. {
  165. CGLIB$STATIC_CALLBACKS = paramArrayOfCallback;
  166. }
  167. private static final void CGLIB$BIND_CALLBACKS(Object paramObject)
  168. {
  169. // Byte code:
  170. // 0: aload_0
  171. // 1: checkcast 2 com/boothsun/zk/dynamic/HelloImpl$$EnhancerByCGLIB$$7b44e866
  172. // 4: astore_1
  173. // 5: aload_1
  174. // 6: getfield 188 com/boothsun/zk/dynamic/HelloImpl$$EnhancerByCGLIB$$7b44e866:CGLIB$BOUND Z
  175. // 9: ifne +43 -> 52
  176. // 12: aload_1
  177. // 13: iconst_1
  178. // 14: putfield 188 com/boothsun/zk/dynamic/HelloImpl$$EnhancerByCGLIB$$7b44e866:CGLIB$BOUND Z
  179. // 17: getstatic 27 com/boothsun/zk/dynamic/HelloImpl$$EnhancerByCGLIB$$7b44e866:CGLIB$THREAD_CALLBACKS Ljava/lang/ThreadLocal;
  180. // 20: invokevirtual 191 java/lang/ThreadLocal:get ()Ljava/lang/Object;
  181. // 23: dup
  182. // 24: ifnonnull +15 -> 39
  183. // 27: pop
  184. // 28: getstatic 186 com/boothsun/zk/dynamic/HelloImpl$$EnhancerByCGLIB$$7b44e866:CGLIB$STATIC_CALLBACKS [Lorg/springframework/cglib/proxy/Callback;
  185. // 31: dup
  186. // 32: ifnonnull +7 -> 39
  187. // 35: pop
  188. // 36: goto +16 -> 52
  189. // 39: checkcast 192 [Lorg/springframework/cglib/proxy/Callback;
  190. // 42: aload_1
  191. // 43: swap
  192. // 44: iconst_0
  193. // 45: aaload
  194. // 46: checkcast 52 org/springframework/cglib/proxy/MethodInterceptor
  195. // 49: putfield 40 com/boothsun/zk/dynamic/HelloImpl$$EnhancerByCGLIB$$7b44e866:CGLIB$CALLBACK_0 Lorg/springframework/cglib/proxy/MethodInterceptor;
  196. // 52: return
  197. }
  198. public Object newInstance(Callback[] paramArrayOfCallback)
  199. {
  200. CGLIB$SET_THREAD_CALLBACKS(paramArrayOfCallback);
  201. CGLIB$SET_THREAD_CALLBACKS(null);
  202. return new 7b44e866();
  203. }
  204. public Object newInstance(Callback paramCallback)
  205. {
  206. CGLIB$SET_THREAD_CALLBACKS(new Callback[] { paramCallback });
  207. CGLIB$SET_THREAD_CALLBACKS(null);
  208. return new 7b44e866();
  209. }
  210. public Object newInstance(Class[] paramArrayOfClass, Object[] paramArrayOfObject, Callback[] paramArrayOfCallback)
  211. {
  212. CGLIB$SET_THREAD_CALLBACKS(paramArrayOfCallback);
  213. Class[] tmp9_8 = paramArrayOfClass;
  214. switch (tmp9_8.length)
  215. {
  216. case 0:
  217. tmp9_8;
  218. break;
  219. default:
  220. new 7b44e866();
  221. throw new IllegalArgumentException("Constructor not found");
  222. }
  223. CGLIB$SET_THREAD_CALLBACKS(null);
  224. }
  225. public Callback getCallback(int paramInt)
  226. {
  227. CGLIB$BIND_CALLBACKS(this);
  228. switch (paramInt)
  229. {
  230. case 0:
  231. break;
  232. }
  233. return null;
  234. }
  235. public void setCallback(int paramInt, Callback paramCallback)
  236. {
  237. switch (paramInt)
  238. {
  239. case 0:
  240. this.CGLIB$CALLBACK_0 = ((MethodInterceptor)paramCallback);
  241. break;
  242. }
  243. }
  244. public Callback[] getCallbacks()
  245. {
  246. CGLIB$BIND_CALLBACKS(this);
  247. return new Callback[] { this.CGLIB$CALLBACK_0 };
  248. }
  249. public void setCallbacks(Callback[] paramArrayOfCallback)
  250. {
  251. this.CGLIB$CALLBACK_0 = ((MethodInterceptor)paramArrayOfCallback[0]);
  252. }
  253. static
  254. {
  255. CGLIB$STATICHOOK1();
  256. }
  257. }
  258. // 2
  259. public class HelloImpl$$FastClassByCGLIB$$767b95f1 extends FastClass
  260. {
  261. public HelloImpl$$FastClassByCGLIB$$767b95f1(Class paramClass)
  262. {
  263. super(paramClass);
  264. }
  265. public int getIndex(Signature paramSignature)
  266. {
  267. String tmp4_1 = paramSignature.toString();
  268. switch (tmp4_1.hashCode())
  269. {
  270. case -1853413644:
  271. if (tmp4_1.equals("say(Ljava/lang/String;)V"))
  272. return 0;
  273. break;
  274. case 1826985398:
  275. case 1913648695:
  276. case 1984935277:
  277. }
  278. }
  279. public int getIndex(String paramString, Class[] paramArrayOfClass)
  280. {
  281. String tmp3_0 = paramString;
  282. switch (tmp3_0.hashCode())
  283. {
  284. case -1776922004:
  285. if (tmp3_0.equals("toString"))
  286. {
  287. Class[] tmp56_1 = paramArrayOfClass;
  288. switch (tmp56_1.length)
  289. {
  290. case 0:
  291. tmp56_1;
  292. return 2;
  293. }
  294. }
  295. break;
  296. case -1295482945:
  297. case 113643:
  298. case 147696667:
  299. }
  300. }
  301. public int getIndex(Class[] paramArrayOfClass)
  302. {
  303. Class[] tmp1_0 = paramArrayOfClass;
  304. switch (tmp1_0.length)
  305. {
  306. case 0:
  307. tmp1_0;
  308. return 0;
  309. }
  310. break label26;
  311. }
  312. // ERROR //
  313. public Object invoke(int paramInt, Object paramObject, Object[] paramArrayOfObject)
  314. throws InvocationTargetException
  315. {
  316. // Byte code:
  317. // 0: aload_2
  318. // 1: checkcast 61 com/boothsun/zk/dynamic/HelloImpl
  319. // 4: iload_1
  320. // 5: tableswitch default:+73 -> 78, 0:+31->36, 1:+42->47, 2:+57->62, 3:+61->66
  321. // 37: iconst_0
  322. // 38: aaload
  323. // 39: checkcast 37 java/lang/String
  324. // 42: invokevirtual 64 com/boothsun/zk/dynamic/HelloImpl:say (Ljava/lang/String;)V
  325. // 45: aconst_null
  326. // 46: areturn
  327. // 47: aload_3
  328. // 48: iconst_0
  329. // 49: aaload
  330. // 50: invokevirtual 65 com/boothsun/zk/dynamic/HelloImpl:equals (Ljava/lang/Object;)Z
  331. // 53: new 67 java/lang/Boolean
  332. // 56: dup_x1
  333. // 57: swap
  334. // 58: invokespecial 70 java/lang/Boolean:<init> (Z)V
  335. // 61: areturn
  336. // 62: invokevirtual 71 com/boothsun/zk/dynamic/HelloImpl:toString ()Ljava/lang/String;
  337. // 65: areturn
  338. // 66: invokevirtual 72 com/boothsun/zk/dynamic/HelloImpl:hashCode ()I
  339. // 69: new 74 java/lang/Integer
  340. // 72: dup_x1
  341. // 73: swap
  342. // 74: invokespecial 77 java/lang/Integer:<init> (I)V
  343. // 77: areturn
  344. // 78: goto +12 -> 90
  345. // 81: new 59 java/lang/reflect/InvocationTargetException
  346. // 84: dup_x1
  347. // 85: swap
  348. // 86: invokespecial 82 java/lang/reflect/InvocationTargetException:<init> (Ljava/lang/Throwable;)V
  349. // 89: athrow
  350. // 90: new 84 java/lang/IllegalArgumentException
  351. // 93: dup
  352. // 94: ldc 86
  353. // 96: invokespecial 88 java/lang/IllegalArgumentException:<init> (Ljava/lang/String;)V
  354. // 99: athrow
  355. //
  356. // Exception table:
  357. // from to target type
  358. // 5 81 81 java/lang/Throwable
  359. }
  360. public Object newInstance(int paramInt, Object[] paramArrayOfObject)
  361. throws InvocationTargetException
  362. {
  363. try
  364. {
  365. switch (paramInt)
  366. {
  367. case 0:
  368. return new HelloImpl();
  369. }
  370. }
  371. catch (Throwable localThrowable)
  372. {
  373. throw new InvocationTargetException(localThrowable);
  374. }
  375. throw new IllegalArgumentException("Cannot find matching method/constructor");
  376. }
  377. public int getMaxIndex()
  378. {
  379. return 3;
  380. }
  381. }

首先,我们关注HelloImpl$$EnhancerByCGLIB$$7b44e866这个类。

  1. public class HelloImpl$$EnhancerByCGLIB$$7b44e866 extends HelloImpl
  2. implements Factory

从上面的代码我们知道,CGLIB生成的动态代理类是继承自被代理类,然后重写了里面的方法,并添加了新的方法。在这里我们需要注意一点:如果被代理类被final修改,那么它不可被继承,即不可被代理;同样,如果委托类中存在final修饰的方法,那么该方法也不可被代理。

  1. final void CGLIB$say$0(String paramString) {
  2. super.say(paramString);
  3. }
  4. public final void say(String paramString) {
  5. MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
  6. if (tmp4_1 == null) {
  7. tmp4_1;
  8. CGLIB$BIND_CALLBACKS(this);
  9. }
  10. if (this.CGLIB$CALLBACK_0 != null)
  11. return;
  12. super.say(paramString);
  13. }

代理类会为被代理的方法生成两个相关的方法,一个是重写的say方法,另一个是CGLIB$say$0方法,我们可以看到后者是直接调用父类中的同样方法。下面,我们重点分析say方法,看看是怎么实现代理功能的。

当调用代理类的say方法时,会先判断是否已经存在了MethodInterceptor接口的拦截器,如果没有的话就调用CGLIB$BIND_CALLBACKS(this);方法来获取Interceptor对象,CGLIB$BIND_CALLBACKS的反编译结果如下:

  1. private static final void CGLIB$BIND_CALLBACKS(java.lang.Object);
  2. Code:
  3. 0: aload_0
  4. 1: checkcast #2; //class net/sf/cglib/test/Target$$EnhancerByCGLIB$$788444a0
  5. 4: astore_1
  6. 5: aload_1
  7. 6: getfield #212; //Field CGLIB$BOUND:Z
  8. 9: ifne 52
  9. 12: aload_1
  10. 13: iconst_1
  11. 14: putfield #212; //Field CGLIB$BOUND:Z
  12. 17: getstatic #24; //Field CGLIB$THREAD_CALLBACKS:Ljava/lang/ThreadLocal;
  13. 20: invokevirtual #215; //Method java/lang/ThreadLocal.get:()Ljava/lang/Object;
  14. 23: dup
  15. 24: ifnonnull 39
  16. 27: pop
  17. 28: getstatic #210; //Field CGLIB$STATIC_CALLBACKS:[Lnet/sf/cglib/proxy/Callback;
  18. 31: dup
  19. 32: ifnonnull 39
  20. 35: pop
  21. 36: goto 52
  22. 39: checkcast #216; //class "[Lnet/sf/cglib/proxy/Callback;"
  23. 42: aload_1
  24. 43: swap
  25. 44: iconst_0
  26. 45: aaload
  27. 46: checkcast #48; //class net/sf/cglib/proxy/MethodInterceptor
  28. 49: putfield #36; //Field CGLIB$CALLBACK_0:Lnet/sf/cglib/proxy/MethodInterceptor;
  29. 52: return

为了方便阅读,等价的代码如下:

  1. private static final void CGLIB$BIND_CALLBACKS(Object o){
  2. Target$$EnhancerByCGLIB$$788444a0 temp_1 = (Target$$EnhancerByCGLIB$$788444a0)o;
  3. Object temp_2;
  4. Callback[] temp_3
  5. if(temp_1.CGLIB$BOUND == true){
  6. return;
  7. }
  8. temp_1.CGLIB$BOUND = true;
  9. temp_2 = CGLIB$THREAD_CALLBACKS.get();
  10. if(temp_2!=null){
  11. temp_3 = (Callback[])temp_2;
  12. }
  13. else if(CGLIB$STATIC_CALLBACKS!=null){
  14. temp_3 = CGLIB$STATIC_CALLBACKS;
  15. }
  16. else{
  17. return;
  18. }
  19. temp_1.CGLIB$CALLBACK_0 = (MethodInterceptor)temp_3[0];
  20. return
  21. }

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方法进行代理 底层将方法全部存入一个数组中,通过数组索引直接进行方法调用
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注