@joshsulin
2018-08-31T08:26:37.000000Z
字数 5928
阅读 1322
动态代理
很简单就是想把JDK动态代理技术以自己的语言描述清楚。
关于概念的解释, 比如 什么是代理、什么是静态代理、什么是动态代理 大家就各自网上搜索了, 这里不做过多解释。
比如 Spring AOP, 其中最常用的 @Transactional, 该注解作用于方法, 当执行该方法开始时, 开启事务, 当执行该方法结束时, 关闭事务。其它的还有类似@Cacheable 等等。
以下这张图展示了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里面定义的方法, 用于实现被代理类执行之后需要完成的事情。
直接上代码
// 定义UserService接口public interface UserService {String execute();}
// 定义被代理者类public class UserServiceImpl implements UserService {@Overridepublic String execute() {System.out.println("执行业务逻辑方法 ==> 2 step");return null;}}
// 定义InvocationHandler接口实现类, 具体代理逻辑实现类public class MyInvocationHandler implements InvocationHandler {private UserService userService;public MyInvocationHandler(UserService userService) {this.userService = userService;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {before();method.invoke(userService, args);after();return null;}private void before() {System.out.println("执行业务逻辑前, 需要执行开启事务功能 ==> 1 step");}private void after() {System.out.println("执行业务逻辑后, 需要执行关闭事务功能 ==> 3 step");}}
// 定义测试入口类, Proxy.newProxyInstance 方法能生成代理类public class MyTest {public static void main(String[] args) {UserService proxy = (UserService) Proxy.newProxyInstance(MyTest.class.getClassLoader(),new Class<?>[]{UserService.class}, new MyInvocationHandler(new UserServiceImpl()));proxy.execute();}}
通过示例程序, 能够明白 Proxy.newProxyInstance 方法才是最重要的, 它能在程序运行过程中动态的生成代理类。
接下来, 我们把动态生成的代理类的字节码保存到class文件里面, 然后再通过反编码成.java文件, 研究一下动态生成的代理类长什么样子。
这里引入一个类 ProxyGenerator, 它有一个方法 generateProxyClass, 用于生成代理类的字节码。
byte[] $Proxy0s = ProxyGenerator.generateProxyClass("$Proxy0", new Class<?>[]{UserService.class});FileOutputStream fileOutputStream;fileOutputStream = new FileOutputStream("$Proxy0.class");fileOutputStream.write($Proxy0s);
生成的.class文件, 通过反编译过后的 java 源程序如下:
import com.josh.proxy.UserService;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.lang.reflect.UndeclaredThrowableException;public final class $Proxy0extends Proxyimplements UserService {private static Method m1;private static Method m2;private static Method m3;private static Method m0;public $Proxy0(InvocationHandler invocationHandler) throws {super(invocationHandler);}public final boolean equals(Object object) throws {try {return (Boolean)this.h.invoke(this, m1, new Object[]{object});}catch (Error | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}public final String toString() throws {try {return (String)this.h.invoke(this, m2, null);}catch (Error | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}public final String execute() throws {try {return (String)this.h.invoke(this, m3, null);}catch (Error | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}public final int hashCode() throws {try {return (Integer)this.h.invoke(this, m0, null);}catch (Error | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}static {try {m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);m3 = Class.forName("com.josh.proxy.UserService").getMethod("execute", new Class[0]);m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);return;}catch (NoSuchMethodException noSuchMethodException) {throw new NoSuchMethodError(noSuchMethodException.getMessage());}catch (ClassNotFoundException classNotFoundException) {throw new NoClassDefFoundError(classNotFoundException.getMessage());}}}
接下来对动态生成的代理类源代码进行初步分析。
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动态代理到底是怎么一回事了. 整个过程中最重要的方法其实是 Proxy.newProxyInstance(), 那它内部到底做了什么事情? 值得我们去深入探索。
在深入探索这个方法源码之前, 我们思考一下如果是我们来实现, 我们应该如何实现?
Poryx.newProxyInstance() 方法作用: 运行时生成代理类的实例。既然返回类的实例, 肯定得有实例对应的类, 类又是如何生成的呢? 是不是可以用流的方式, 将字符串拼接好写到.java文件里面。
第一步: 用字符串的形式拼凑出内存里面代理类。
第二步: 把字符串的类输出到一个文件(.java)里面。
第三步: 把.java文件编译。
第四步: 把对应编译出来的字节码文件加载到jvm内存中。
动态代理最主要功能就是生成动态类 Proxy.newProxyInstance(), 分析源代码就从这个方法入手。直接上代码, 或者打一个断点跟进去。
// 生成一个代理对象@CallerSensitivepublic static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) throws IllegalArgumentException {............/** Look up or generate the designated proxy class.* 遍历查找己经构建好了的class.*/Class<?> cl = getProxyClass0(loader, intfs);............}private static Class<?> getProxyClass0(ClassLoader loader,Class<?>... interfaces) {............/**If the proxy class defined by the given loader implementingthe given interfaces exists, this will simply return the cached copy; otherwise, it will create the proxy class via the ProxyClassFactory。proxyClassCahce 它是动态代理对象proxyClass的缓存, 只是class的缓存, 并不是对象的缓存。如何去取呢?1、从classLoader里面去取。2、如果没有, 从字节码缓存里面去取。3、如果都不存在, 就直接通过ProxyClassFactory去创建。*/return proxyClassCache.get(loader, interfaces);}