JDK动态代理
JDK动态代理核心是两个类:InvocationHandler
和 Proxy
举个栗子
为便于理解,首先看一个例子:
希望实现这样一个功能:使用 UserService
时,只需关注自己的核心业务逻辑的实现,对于日志功能的打印,由系统的公共服务完成。
首先定义一个业务类的接口:UserService.java
1 | java复制代码package com.proxy; |
实现该接口:
1 | java复制代码package com.proxy; |
定义一个InvocationHandler
实现类,该实现类有我们想要添加的公共日志
1 | java复制代码package com.proxy; |
接下来看看如何在客户端中调用
1 | java复制代码/** |
开始一步步分析源码
源码分析
1.Proxy.newProxyInstance( ClassLoader loader, Class[] interfaces, InvocationHandler h)
Proxy.newProxyInstance( ClassLoader loader, Class[] interfaces, InvocationHandler h)
产生了代理对象,首先分析下方法的三个参数
ClassLoader loader
:类加载器,类加载器用于加载代理类。这里经过测试发现不论是传userService的类加载器,还是logHandler的类加载器,得到的结果都是一样。Class[] interfaces
:被代理类实现的接口集合。有了它,代理类就可以实现被代理类的所有接口。InvocationHandler
h:handler对象,不能为null。
接下来看一下方法的具体实现:
1 | java复制代码 public static Object newProxyInstance(ClassLoader loader, |
这段代码核心就是通过getProxyClass0(loader, intfs)
得到代理类的Class对象,然后通过Class对象得到构造方法,进而创建代理对象。下一步看getProxyClass0
这个方法。
2.getProxyClass0
1 | java复制代码//此方法也是Proxy类下的方法 |
3. proxyClassCache.get
这里看到 proxyClassCache
,有Cache便知道是缓存的意思,正好呼应了前面Look up or generate the designated proxy class。查询(在缓存中已经有)或生成指定的代理类的class对象这段注释。proxyClassCache
是个WeakCache
类的对象,调用proxyClassCache.get(loader, interfaces)
; 可以得到缓存的代理类或创建代理类(没有缓存的情况)。先看下WeakCache
类的定义(这里先只给出变量的定义和构造函数):
1 | java复制代码//K代表key的类型,P代表参数的类型,V代表value的类型。 |
其中map变量是实现缓存的核心变量,他是一个双重的Map结构: (key, sub-key) -> value。其中key是传进来的Classloader进行包装后的对象,sub-key是由WeakCache构造函数传入的KeyFactory()生成的。value就是产生代理类的对象,是由WeakCache构造函数传入的ProxyClassFactory()生成的。如下,回顾一下:
1 | java复制代码private static final WeakCache<ClassLoader, Class<?>[], Class<?>> |
产生sub-key的KeyFactory代码如下,这个我们不去深究,只要知道他是根据传入的ClassLoader和接口类生成sub-key即可。
1 | java复制代码private static final class KeyFactory |
通过sub-key拿到一个Supplier<Class<?>>
对象,然后调用这个对象的get方法,最终得到代理类的Class对象。
好,大体上说完 WeakCache
这个类的作用,我们回到刚才proxyClassCache.get(loader, interfaces)
;这句代码。get是 WeakCache
里的方法。源码如下:
1 | java复制代码//K和P就是WeakCache定义中的泛型,key是类加载器,parameter是接口类数组 |
4.Factory.get
supplier就是factory,所以接下来我们看看Factory类(Factory是WeakCache的内部类)的get方法。
1 | java复制代码public synchronized V get() { // serialize access |
5.ProxyClassFactory.apply
最后来到 ProxyClassFactory
的 apply 方法,代理类就是在这里生成的。
1 | java复制代码//这里的BiFunction<T, U, R>是个函数式接口,可以理解为用T,U两种类型做参数,得到R类型的返回值 |
6. 字节码文件分析
到这里其实已经分析完了,但是本着深究的态度,决定看看JDK生成的动态代理字节码是什么,于是将字节码保存到磁盘上的class文件中。代码如下:
1 | java复制代码package com.sun.proxy; |
最后
- 如果觉得有收获,三连支持下;
- 文章若有错误,欢迎评论留言指出,也欢迎转载,转载请注明出处;
- 个人vx:Listener27, 交流技术、面试、学习资料、帮助一线互联网大厂内推等
本文转载自: 掘金