JDK动态代理实现原理

桃扇骨 2022-10-02 10:50 271阅读 0赞

JDK动态代理实现原理

动态代理机制

  • 通过实现 InvocationHandler 接口创建自己的调用处理器
  • 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类
  • 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型
  • 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入

Interface InvocationHandler

该接口中仅定义了一个方法Object:invoke(Object obj,Method method,Object[] args)。在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,args为该方法的参数数组。这个抽象方法在代理类中动态实现。

Proxy

  • 该类即为动态代理类
  • Protected Proxy(InvocationHandler h)

    • 构造函数,用于给内部的h赋值
  • Static Class getProxyClass (ClassLoader loader,Class[] interfaces)

    • 获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组
  • Static Object newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h)

    • 返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在Subject接口中声明过的方法)
  • Dynamic Proxy

    • 它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后该class就宣称它实现了这些 interface。你当然可以把该class的实例当作这些interface中的任何一个来用。当然啦,这个Dynamic Proxy其实就是一个Proxy,它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。

代码示例

创建接口

  1. /** * @ClassName: BuyService * @Description: 购买基础接口 * @Author: 尚先生 * @CreateDate: 2019/6/17 14:52 * @Version: 1.0 */
  2. public interface BuyService {
  3. String buyPhone();
  4. String buyComputer();
  5. }

创建实现类

  1. /** * @ClassName: BuyServiceImpl * @Description: 购买实现类 * @Author: 尚先生 * @CreateDate: 2019/6/17 14:53 * @Version: 1.0 */
  2. public class BuyServiceImpl implements BuyService {
  3. @Intercept("buyPhone")
  4. @Override
  5. public String buyPhone() {
  6. try {
  7. TimeUnit.SECONDS.sleep(1);
  8. } catch (InterruptedException e) {
  9. e.printStackTrace();
  10. }
  11. System.out.println("==========BuyServiceImpl.class=============" + " buyPhone");
  12. this.buyComputer();
  13. return "buy phone";
  14. }
  15. @Intercept("buyComputer")
  16. @Override
  17. public String buyComputer() {
  18. try {
  19. TimeUnit.SECONDS.sleep(1);
  20. } catch (InterruptedException e) {
  21. e.printStackTrace();
  22. }
  23. System.out.println("==========BuyServiceImpl.class=============" + " buyComputer");
  24. return "buy computer";
  25. }
  26. }

创建 InvocationHandler

  1. /** * @ClassName: ReflectionHandler * @Description: 自定义处理handler * @Author: 尚先生 * @CreateDate: 2019/6/17 14:54 * @Version: 1.0 */
  2. public class ReflectionHandler implements InvocationHandler {
  3. private Object target;
  4. public ReflectionHandler(Object target) {
  5. this.target = target;
  6. }
  7. public <T> T getProxy(){
  8. return (T) Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
  9. }
  10. @Override
  11. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  12. return method.invoke(target,args);
  13. }
  14. }

创建启动类

  1. /** * @ClassName: Bootstrap * @Description: 启动测试类 * @Author: 尚先生 * @CreateDate: 2019/6/17 14:58 * @Version: 1.0 */
  2. public class Bootstrap {
  3. public static void main(String[] args) {
  4. // 动态代理实现
  5. ReflectionHandler reflectionHandler = new ReflectionHandler(new BuyServiceImpl());
  6. BuyService proxy = reflectionHandler.getProxy();
  7. String computer = proxy.buyComputer();
  8. String phone = proxy.buyPhone();
  9. System.out.println(computer + "\r\n" + phone);
  10. }

运行结果

  1. // 修改前执行结果
  2. ========BuyServiceImpl.class=========== buyComputer
  3. ========BuyServiceImpl.class=========== buyPhone
  4. =======BuyServiceImpl.class=========== buyComputer
  5. buy computer
  6. buy phone

代理类字节码文件

  1. /** * 代理类字节码文件,反编译结果 * @Author: 尚先生 * @CreateDate: 2019/6/17 14:54 * @Version: 1.0 */
  2. import com.learn.demo.java.proxy.BuyService;
  3. import java.lang.reflect.InvocationHandler;
  4. import java.lang.reflect.Method;
  5. import java.lang.reflect.Proxy;
  6. import java.lang.reflect.UndeclaredThrowableException;
  7. public final class ProxyBuyService
  8. extends Proxy
  9. implements BuyService
  10. {
  11. private static Method m1;
  12. private static Method m2;
  13. private static Method m3;
  14. private static Method m4;
  15. private static Method m0;
  16. public ProxyBuyService(InvocationHandler paramInvocationHandler)
  17. throws
  18. {
  19. super(paramInvocationHandler);
  20. }
  21. public final boolean equals(Object paramObject)
  22. throws
  23. {
  24. try
  25. {
  26. return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
  27. }
  28. catch (Error|RuntimeException localError)
  29. {
  30. throw localError;
  31. }
  32. catch (Throwable localThrowable)
  33. {
  34. throw new UndeclaredThrowableException(localThrowable);
  35. }
  36. }
  37. public final String toString()
  38. throws
  39. {
  40. try
  41. {
  42. return (String)this.h.invoke(this, m2, null);
  43. }
  44. catch (Error|RuntimeException localError)
  45. {
  46. throw localError;
  47. }
  48. catch (Throwable localThrowable)
  49. {
  50. throw new UndeclaredThrowableException(localThrowable);
  51. }
  52. }
  53. public final String buyPhone(BuyService paramBuyService)
  54. throws
  55. {
  56. try
  57. {
  58. return (String)this.h.invoke(this, m3, new Object[] { paramBuyService });
  59. }
  60. catch (Error|RuntimeException localError)
  61. {
  62. throw localError;
  63. }
  64. catch (Throwable localThrowable)
  65. {
  66. throw new UndeclaredThrowableException(localThrowable);
  67. }
  68. }
  69. public final String buyComputer(BuyService paramBuyService)
  70. throws
  71. {
  72. try
  73. {
  74. return (String)this.h.invoke(this, m4, new Object[] { paramBuyService });
  75. }
  76. catch (Error|RuntimeException localError)
  77. {
  78. throw localError;
  79. }
  80. catch (Throwable localThrowable)
  81. {
  82. throw new UndeclaredThrowableException(localThrowable);
  83. }
  84. }
  85. public final int hashCode()
  86. throws
  87. {
  88. try
  89. {
  90. return ((Integer)this.h.invoke(this, m0, null)).intValue();
  91. }
  92. catch (Error|RuntimeException localError)
  93. {
  94. throw localError;
  95. }
  96. catch (Throwable localThrowable)
  97. {
  98. throw new UndeclaredThrowableException(localThrowable);
  99. }
  100. }
  101. static
  102. {
  103. try
  104. {
  105. m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
  106. m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
  107. m3 = Class.forName("com.learn.demo.java.proxy.BuyService").getMethod("buyPhone", new Class[] { Class.forName("com.learn.demo.java.proxy.BuyService") });
  108. m4 = Class.forName("com.learn.demo.java.proxy.BuyService").getMethod("buyComputer", new Class[] { Class.forName("com.learn.demo.java.proxy.BuyService") });
  109. m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
  110. return;
  111. }
  112. catch (NoSuchMethodException localNoSuchMethodException)
  113. {
  114. throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
  115. }
  116. catch (ClassNotFoundException localClassNotFoundException)
  117. {
  118. throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
  119. }
  120. }
  121. }

代码流程分析

  • 具体的实现流程如同上述代码,创建InvocationHandler对象,根据Proxy创建代理对象,通过代理对象执行目标方法,返回执行结果。
  • 从创建接口到默认实现类,我们可以通过直接new 实现类的方式去实现,但是有时候我们比不知道具体实现类是哪个,或者说实现类自身去做会很大程度上增加代码的耦合性。这时候我们采用代理的方式去增强代码的可用性。
  • 以上是JDK动态代理的实际实现,但是在执行结果中看不到buyPhone() 调用buyComputer()的实际调用类信息,实际执行信息打印已经在GitHub中更新,更多详情可关注dwyanewede。

    // 修改后的执行结果,详见GitHub
    ========当前类描述 com.sun.proxy.$Proxy0=========== buyComputer
    ========当前类描述 com.sun.proxy.$Proxy0=========== buyPhone
    =======当前类描述 com.learn.demo.java.proxy.BuyServiceImpl=========== buyComputer
    buy computer
    buy phone

为什么要关注这一点呢,因为这一点跟我们在Spring Framework框架中一些AOP切面拦截密切相关,具体在Spring Framework框架中的实现,可参见下一篇文章:JDK动态代理在Spring AOP中的实现

更多优秀文章

JDK动态代理在Spring AOP中的实现
https://blog.csdn.net/shang_xs/article/details/92778364
java界的小学生
https://blog.csdn.net/shang_xs

公众号推荐

在这里插入图片描述

发表评论

表情:
评论列表 (有 0 条评论,271人围观)

还没有评论,来说两句吧...

相关阅读

    相关 JDK动态代理实现原理

    之前虽然会用JDK的动态代理,但是有些问题却一直没有搞明白。比如说:InvocationHandler的invoke方法是由谁来调用的,代理对象是怎么生成的,直到前几个星期才把

    相关 jdk动态代理实现原理

    jdk动态代理实际上是jvm帮我们建成的类实现我们传入的接口,加载字节码到jvm。实际上就是帮我们拼接的字符串和我们传进去的接口进行拼接,然后生成字节码加载进虚拟机。代理类持有

    相关 jdk动态代理实现原理总结

    运行时创建一个代理对象的步骤为 1. 生成代理类的字节码 2. 加载字节码,创建代理对象 首先,代理对象的目的是在被调用方法的时候既能实现被代理对象的方法,又能够增加自