[关闭]
@joshsulin 2018-08-31T08:26:37.000000Z 字数 5928 阅读 1322

JDK动态代理如何使用及底层实现原理

动态代理


为什么要写这篇博客?

很简单就是想把JDK动态代理技术以自己的语言描述清楚。

什么是JDK动态代理及哪些功能是基于JDK动态代理实现的?

什么是JDK动态代理?

关于概念的解释, 比如 什么是代理什么是静态代理什么是动态代理 大家就各自网上搜索了, 这里不做过多解释。

哪些功能是基于JDK动态代理实现?

比如 Spring AOP, 其中最常用的 @Transactional, 该注解作用于方法, 当执行该方法开始时, 开启事务, 当执行该方法结束时, 关闭事务。其它的还有类似@Cacheable 等等。

JDK动态代理涉及到哪些角色(类)及它们是如何运转及调用关系?

以下这张图展示了JDK动态代理各类及方法的调用关系, 图中各类前有编号, 按照编号顺序讲解一下。
此处输入图片的描述

1、被代理类(UserServiceImpl): 很容易理解, 即使我们讲的是JDK动态代理, 肯定要有被代理类啊, 其实就是正常业务逻辑类。
2、接口(UserService): 定义一个统一接口, 现在都是基于接口编程, 肯定要有一个接口。其实JDK动态代理规定: 被代理类和代理类必须实现同一个接口。
3、代理类($Proxy0): 这是一个动态生成的类, 程序运行过程中生成并保存在内存中。通过Proxy.newProxyInstance来生成的
4、类Proxy, 这是代理类需要继承的类, 代理类动态生成过程中继承Proxy类。
5、类MyInvocationHandler: 我们需要在该类invoke方法里实现被代理后的逻辑。
6、接口InvocationHandler: 通过Proxy类可以看到, h.invoke其实是调用实现了接口InvocationHandler的invoke方法。
7、before方法: 在类MyInvocationHandler里面定义的方法, 用于实现被代理类执行之前需要完成的事情。
8、after方法: 在类MyInvocationHandlerj里面定义的方法, 用于实现被代理类执行之后需要完成的事情。

JDK动态代理demo示例程序

直接上代码

  1. // 定义UserService接口
  2. public interface UserService {
  3. String execute();
  4. }
  1. // 定义被代理者类
  2. public class UserServiceImpl implements UserService {
  3. @Override
  4. public String execute() {
  5. System.out.println("执行业务逻辑方法 ==> 2 step");
  6. return null;
  7. }
  8. }
  1. // 定义InvocationHandler接口实现类, 具体代理逻辑实现类
  2. public class MyInvocationHandler implements InvocationHandler {
  3. private UserService userService;
  4. public MyInvocationHandler(UserService userService) {
  5. this.userService = userService;
  6. }
  7. @Override
  8. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  9. before();
  10. method.invoke(userService, args);
  11. after();
  12. return null;
  13. }
  14. private void before() {
  15. System.out.println("执行业务逻辑前, 需要执行开启事务功能 ==> 1 step");
  16. }
  17. private void after() {
  18. System.out.println("执行业务逻辑后, 需要执行关闭事务功能 ==> 3 step");
  19. }
  20. }
  1. // 定义测试入口类, Proxy.newProxyInstance 方法能生成代理类
  2. public class MyTest {
  3. public static void main(String[] args) {
  4. UserService proxy = (UserService) Proxy.newProxyInstance(MyTest.class.getClassLoader(),
  5. new Class<?>[]{UserService.class}, new MyInvocationHandler(new UserServiceImpl()));
  6. proxy.execute();
  7. }
  8. }

通过示例程序, 能够明白 Proxy.newProxyInstance 方法才是最重要的, 它能在程序运行过程中动态的生成代理类。

JDK动态代理技术中最重要的代理类源代码分析

接下来, 我们把动态生成的代理类的字节码保存到class文件里面, 然后再通过反编码成.java文件, 研究一下动态生成的代理类长什么样子。

这里引入一个类 ProxyGenerator, 它有一个方法 generateProxyClass, 用于生成代理类的字节码。

  1. byte[] $Proxy0s = ProxyGenerator.generateProxyClass("$Proxy0", new Class<?>[]{UserService.class});
  2. FileOutputStream fileOutputStream;
  3. fileOutputStream = new FileOutputStream("$Proxy0.class");
  4. fileOutputStream.write($Proxy0s);

生成的.class文件, 通过反编译过后的 java 源程序如下:

  1. import com.josh.proxy.UserService;
  2. import java.lang.reflect.InvocationHandler;
  3. import java.lang.reflect.Method;
  4. import java.lang.reflect.Proxy;
  5. import java.lang.reflect.UndeclaredThrowableException;
  6. public final class $Proxy0
  7. extends Proxy
  8. implements UserService {
  9. private static Method m1;
  10. private static Method m2;
  11. private static Method m3;
  12. private static Method m0;
  13. public $Proxy0(InvocationHandler invocationHandler) throws {
  14. super(invocationHandler);
  15. }
  16. public final boolean equals(Object object) throws {
  17. try {
  18. return (Boolean)this.h.invoke(this, m1, new Object[]{object});
  19. }
  20. catch (Error | RuntimeException throwable) {
  21. throw throwable;
  22. }
  23. catch (Throwable throwable) {
  24. throw new UndeclaredThrowableException(throwable);
  25. }
  26. }
  27. public final String toString() throws {
  28. try {
  29. return (String)this.h.invoke(this, m2, null);
  30. }
  31. catch (Error | RuntimeException throwable) {
  32. throw throwable;
  33. }
  34. catch (Throwable throwable) {
  35. throw new UndeclaredThrowableException(throwable);
  36. }
  37. }
  38. public final String execute() throws {
  39. try {
  40. return (String)this.h.invoke(this, m3, null);
  41. }
  42. catch (Error | RuntimeException throwable) {
  43. throw throwable;
  44. }
  45. catch (Throwable throwable) {
  46. throw new UndeclaredThrowableException(throwable);
  47. }
  48. }
  49. public final int hashCode() throws {
  50. try {
  51. return (Integer)this.h.invoke(this, m0, null);
  52. }
  53. catch (Error | RuntimeException throwable) {
  54. throw throwable;
  55. }
  56. catch (Throwable throwable) {
  57. throw new UndeclaredThrowableException(throwable);
  58. }
  59. }
  60. static {
  61. try {
  62. m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
  63. m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
  64. m3 = Class.forName("com.josh.proxy.UserService").getMethod("execute", new Class[0]);
  65. m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
  66. return;
  67. }
  68. catch (NoSuchMethodException noSuchMethodException) {
  69. throw new NoSuchMethodError(noSuchMethodException.getMessage());
  70. }
  71. catch (ClassNotFoundException classNotFoundException) {
  72. throw new NoClassDefFoundError(classNotFoundException.getMessage());
  73. }
  74. }
  75. }

接下来对动态生成的代理类源代码进行初步分析。
1、public final class $Proxy0 extends Proxy implements UserService
继承了Proxy类并实现了UserService接口, 理解了为什么 Proxy.newProxyInstance 第二个参数要传入接口了吧? 类可以实现多个接口, 所以第二个参数为数组。
2、代理类实现的接口方法。execute(), 核心代码就一句
return (String)this.h.invoke(this, m3, null);
这里的h是什么, 继承了Proxy类, 里面有 protected InvocationHandler h; h是成员变量, 类型为InvocationHandler, 再观察一下代理类的构造方法。应该理解了为什么Proxy.newProxyInstance第三个参数一定要传入实现了InvocationHandler接口的类的对象。
3、代理类实现的接口方法里, 有 h.invoke(this, m3, null) 这句话。现在应该理解了MyInvocationHandler类里面invoke方法3个参数的含意了吧。
m3 = Class.forName("com.josh.proxy.UserService").getMethod("execute", new Class[0]);
public Object invoke(Object proxy, Method method, Object[] args)。
不做过多解释了, 大家去理解一下吧。

如果我们自己来实现JDK动态代理关键技术点和实现步骤有哪些?

通过上面的讲解, 大家应该了解了JDK动态代理到底是怎么一回事了. 整个过程中最重要的方法其实是 Proxy.newProxyInstance(), 那它内部到底做了什么事情? 值得我们去深入探索。
在深入探索这个方法源码之前, 我们思考一下如果是我们来实现, 我们应该如何实现?

Poryx.newProxyInstance() 方法作用: 运行时生成代理类的实例。既然返回类的实例, 肯定得有实例对应的类, 类又是如何生成的呢? 是不是可以用流的方式, 将字符串拼接好写到.java文件里面。

第一步: 用字符串的形式拼凑出内存里面代理类。
第二步: 把字符串的类输出到一个文件(.java)里面。
第三步: 把.java文件编译。
第四步: 把对应编译出来的字节码文件加载到jvm内存中。

JDK动态代理源码分析

动态代理最主要功能就是生成动态类 Proxy.newProxyInstance(), 分析源代码就从这个方法入手。直接上代码, 或者打一个断点跟进去。

  1. // 生成一个代理对象
  2. @CallerSensitive
  3. public static Object newProxyInstance(ClassLoader loader,
  4. Class<?>[] interfaces,
  5. InvocationHandler h) throws IllegalArgumentException {
  6. ......
  7. ......
  8. /*
  9. * Look up or generate the designated proxy class.
  10. * 遍历查找己经构建好了的class.
  11. */
  12. Class<?> cl = getProxyClass0(loader, intfs);
  13. ......
  14. ......
  15. }
  16. private static Class<?> getProxyClass0(ClassLoader loader,
  17. Class<?>... interfaces) {
  18. ......
  19. ......
  20. /**
  21. If the proxy class defined by the given loader implementing
  22. the given interfaces exists, this will simply return the cached copy; otherwise, it will create the proxy class via the ProxyClassFactory。
  23. proxyClassCahce 它是动态代理对象proxyClass的缓存, 只是class的缓存, 并不是对象的缓存。如何去取呢?
  24. 1、从classLoader里面去取。
  25. 2、如果没有, 从字节码缓存里面去取。
  26. 3、如果都不存在, 就直接通过ProxyClassFactory去创建。
  27. */
  28. return proxyClassCache.get(loader, interfaces);
  29. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注