在 Java 领域, 动态代理应用非常广泛, 特别是流行的 Spring/MyBatis 等框架. JDK 本身是有实现动态代理技术的, 不过要求被代理的类必须实现接口, 不过 cglib 对这一不足进行了有效补充. 本篇博客将涉及 2 个话题:
第一, JDK 动态代理的实现原理, 带你探索动态代理的实质面目;
第二, 自己动手写代码去实现 JDK 动态代理, 去创造世界!
JDK 动态代理
先写一个例子, 感性认识下动态代理~
业务接口:
interface
业务实现类:
interface impl
业务处理类:
Handler
测试类:
test
运行结果:
result
在 JDK 动态代理中涉及如下角色:
业务接口 Interface, 业务实现类 target, 业务处理类 Handler,JVM 在内存中生成的动态代理类 $Proxy0
动态代理原理图:
动态代理的真实面目
说白了, 动态代理的过程是这样的:
第一: Proxy 通过传递给它的参数 (interfaces/invocationHandler) 生成代理类 $Proxy0;
第二: Proxy 通过传递给它的参数 (ClassLoader) 来加载生成的代理类 $Proxy0 的字节码文件;
我们来看看上面例子中生成的 $Proxy0 的模样:
$Proxy0
首先,$Proxy 是实现了我们的业务接口 (Man) 的, 所以客户端显然可以调用业务接口的方法.
其次, 注意到 $Proxy 是继承自 Proxy, 并通过构造方法将业务处理类传入给父类 Proxy 进行初始化.(实质上, 你可以看看源码, 在 Proxy 中存在 protected InvocationHandler h;)
初始化 Proxy
findObject
很明显, 我们看到了业务接口的方法是如何被调用的:
最终都是回调业务处理类 (具体的 Handler) 的 invoke 方法完成调用!
手写代码实现 JDK 动态代理
在上面, 我们已经分析了 JDK 动态代理的整个调用过程, 接下来, 我们就来手写实现它吧!
先来看一眼图:
手写实现 JDK 动态代理
自定义 InvocationHandler:
MyInvocationHandler
实现 MyInvocationHandler 的业务处理 Handler:
MyHandler
自定义类加载器 MyClassLoader:
MyClassLoader
为什么要定义一个自定义的类加载器呢? 它的作用是什么呢?
要知道, 我们是想手写 JDK 动态代理, 那么我们将自己在内存中生成动态代理类, 那么我们如何加载呢? 这时候, 就可以利用自定义的类加载器做到!
上述代码, 重写了 findClass 方法, 就是为了在指定路径下加载指定的字节码文件.
自定义 MyProxy:
MyProxy
MyProxy 的作用就相当于 JDK 的 Proxy.MyProxy 做了哪些事情呢?
第一: 需要根据 interfaces 接口构造出动态代理类需要的方法.(其实就是利用反射获取)
第二: 把动态生成的代理类 (即. java 文件) 进行编译, 生成字节码文件(即. class 文件), 然后利用类加载进行加载
第三: 动态代理类进行加载后, 利用反射机制, 通过构造方法进行实例化, 并在实例化时, 初始化业务 Hanlder
看一下 MyProxy 的其他方法:
编译方法
getMethodString 方法
运行结果
我们来看一眼生成的 $MyProxy0:
$MyProxy0
OK, 到这里, 整个 JDK 的动态代理的实现原理以及手写实现就结束了, 你学到了么?
GoodBye My Friend~
来源: https://juejin.im/entry/5ba3ad06f265da0aeb71011e