Java教程

看源码深入了解JDK动态代理

Java基础 小海豚博客管理员 2019-02-16 00:24:00.0 283 0条

在java中有多种动态代理技术,比如JDK,CGLIB,Javassist,ASM,其中最常用是JDK动态代理和CGLIB。JDK动态代理是JDK自带的,是java.lang.reflect.*包提供的方式,它必须要借用接口才能产生代理对象。我们就深入解析源码了解它是如何生成代理对象,又如何代理逻辑的。

一.首页它要用到接口,先定义一个接口

  1. public interface animal {
  2. public void isAnimal();
  3. }

二.实现类

  1. public class dog implements animal{
  2. public void isAnimal()
  3. {
  4. System.out.println("我是动物");
  5. }
  6. }

三.实现代理逻辑

  1. //这个类是代理类的逻辑,因为在生成的代理类中会调用这个invoke。
  2. public class handle implements InvocationHandler {
  3. private dog d;
  4. public handle(dog d) {
  5. this.d = d;
  6. }
  7. //第一个参数是代理类,第2个是要执行的方法,第3个是参数
  8. @Override
  9. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  10. System.err.println("before");
  11. // 执行某对象的方法,第一个参数是对象,第2个是参数,如果有返回值则返回,没有则null
  12. Object object= method.invoke(d, args);
  13. System.err.println("after");
  14. return object;
  15. }
  16. }

在JDK动态代理中,要实现代理逻辑类必须去实现java.lang.reflect.InvocationHandler接口,它里面定义了一个invoke方法(为什么一定要实现这个接口呢,等一下会说到,因为生成的代理对象默认调用的是这个方法)
四.测试,生成代理对象

  1. public class test {
  2. public static void main(String arg[])
  3. { dog d=new dog();
  4. //生成代理对象,参数分别是:被代理对象的类加载器,被代理对象的接口数组,代理逻辑。
  5. //当然被代理对象的类加载器也可以这样得到d.getClass().getClassLoader()
  6. animal a=(animal)Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),d.getClass().getInterfaces(),new handle(d));
  7. a.isAnimal(); //调用代理对象的isAnimal()方法
  8. }
  9. }

结果是:

  1. before
  2. 我是动物
  3. after
  4. Process finished with exit code 0

其中对于第8行,a.isAnimal();调用代理对象的isAnimal()方法为什么会调用invok()方法呢,因为生成代理对象的isAnimal()方法调用this.invoke()。为了解释这个,我们来了解生成代理对象的过程

五.进入到Proxy类的newProxyInstance方法:

  1. public static Object newProxyInstance(ClassLoader loader,
  2. Class[] interfaces,
  3. InvocationHandler h)
  4. throws IllegalArgumentException
  5. {
  6. if (h == null) {
  7. throw new NullPointerException();
  8. }
  9. /*
  10. * Look up or generate the designated proxy class.
  11. */
  12. Class cl = getProxyClass(loader, interfaces);
  13. /*
  14. * Invoke its constructor with the designated invocation handler.
  15. */
  16. try {
  17. Constructor cons = cl.getConstructor(constructorParams);
  18. return cons.newInstance(new Object[] { h });
  19. } catch (NoSuchMethodException e) {
  20. throw new InternalError(e.toString());
  21. } catch (IllegalAccessException e) {
  22. throw new InternalError(e.toString());
  23. } catch (InstantiationException e) {
  24. throw new InternalError(e.toString());
  25. } catch (InvocationTargetException e) {
  26. throw new InternalError(e.toString());
  27. }
  28. }

关键是这2行:

  1. // 创建代理类
  2. Class cl = getProxyClass(loader, interfaces);
  3. // 实例化代理对象
  4. Constructor cons = cl.getConstructor(constructorParams);

返回的是代理类的实例化对象。接下来的调用就很清晰了。

  那么,JDK动态代理最核心的关键就是这个方法:

  1. Class cl = getProxyClass(loader, interfaces);

进入该方法,这个方法很长,前面很多都是铺垫,在方法的最后调用了一个方法:

  1. //参数分别是:代理对象的类名,被代理对象接口集合
  2. byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
  3. proxyName, interfaces);

这个方法就是生成代理对象的2进制流,我们写入到文件中

  1. public class test {
  2. public static void main(String arg[])
  3. { dog d=new dog();
  4. byte[] bs = ProxyGenerator.generateProxyClass("AudiImpl", d.getClass().getInterfaces());
  5. try {
  6. new FileOutputStream(new File("d:/AudiImpl.class")).write(bs);
  7. }catch (Exception e)
  8. { } }}

并反编译成java文件如:

  1. import java.lang.reflect.InvocationHandler;
  2. import java.lang.reflect.Method;
  3. import java.lang.reflect.Proxy;
  4. import java.lang.reflect.UndeclaredThrowableException;
  5. import me.wuwenbin.noteblogv4.jdktest.animal;
  6. public final class AudiImpl
  7. extends Proxy
  8. implements animal
  9. {
  10. private static Method m1;
  11. private static Method m2;
  12. private static Method m3;
  13. private static Method m0;
  14. public AudiImpl(InvocationHandler paramInvocationHandler)
  15. {
  16. super(paramInvocationHandler);
  17. }
  18. public final boolean equals(Object paramObject)
  19. {
  20. try
  21. {
  22. return ((Boolean)h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
  23. }
  24. catch (Error|RuntimeException localError)
  25. {
  26. throw localError;
  27. }
  28. catch (Throwable localThrowable)
  29. {
  30. throw new UndeclaredThrowableException(localThrowable);
  31. }
  32. }
  33. public final String toString()
  34. {
  35. try
  36. {
  37. return (String)h.invoke(this, m2, null);
  38. }
  39. catch (Error|RuntimeException localError)
  40. {
  41. throw localError;
  42. }
  43. catch (Throwable localThrowable)
  44. {
  45. throw new UndeclaredThrowableException(localThrowable);
  46. }
  47. }
  48. public final void isAnimal()
  49. {
  50. try
  51. {
  52. h.invoke(this, m3, null);
  53. return;
  54. }
  55. catch (Error|RuntimeException localError)
  56. {
  57. throw localError;
  58. }
  59. catch (Throwable localThrowable)
  60. {
  61. throw new UndeclaredThrowableException(localThrowable);
  62. }
  63. }
  64. public final int hashCode()
  65. {
  66. try
  67. {
  68. return ((Integer)h.invoke(this, m0, null)).intValue();
  69. }
  70. catch (Error|RuntimeException localError)
  71. {
  72. throw localError;
  73. }
  74. catch (Throwable localThrowable)
  75. {
  76. throw new UndeclaredThrowableException(localThrowable);
  77. }
  78. }
  79. static
  80. {
  81. try
  82. {
  83. m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
  84. m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
  85. m3 = Class.forName("me.wuwenbin.noteblogv4.jdktest.animal").getMethod("isAnimal", new Class[0]);
  86. m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
  87. return;
  88. }
  89. catch (NoSuchMethodException localNoSuchMethodException)
  90. {
  91. throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
  92. }
  93. catch (ClassNotFoundException localClassNotFoundException)
  94. {
  95. throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
  96. }
  97. }
  98. }

发现不止我们的接口的方法,还有默认的方法也被代理了,我们可以看到53行

  1. public final void isAnimal()
  2. {
  3. try
  4. {
  5. h.invoke(this, m3, null);
  6. return;
  7. }
  8. catch (Error|RuntimeException localError)
  9. {
  10. throw localError;
  11. }
  12. catch (Throwable localThrowable)
  13. {
  14. throw new UndeclaredThrowableException(localThrowable);
  15. }
  16. }

调用了,调用了代理逻辑的invoke()方法,并把方法也传入进去了,利用了方法的反射,执行实现类的当前方法,当然在执行实现类前已经加入了其他执行过程输出。

  1. @Override
  2. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  3. System.err.println("before");
  4. // 执行某对象的方法,第一个参数是对象,第2个是参数,如果有返回值则返回,没有则null
  5. Object object= method.invoke(d, args);
  6. System.err.println("after");
  7. return object;
  8. }
暗锚,解决锚点偏移

文章评论

嘿,来试试登录吧!