目录
1. 前言
JDK
动态代理的应用还是非常广泛的,例如在
Spring、MyBatis
以及
Feign
等很多框架中动态代理都被大量的使用,可以说学好
JDK
动态代理,对于我们阅读这些框架的底层源码还是很有帮助的
2. 一个简单的例子
在分析原因之前,我们先完整的看一下实现
JDK
动态代理需要几个步骤,首先需要定义一个接口
2.1. 定义接口
public interface Worker {
void work();
}
2.2. 接口实现类
public class Programmer implements Worker {
@Override
public void work() {
System.out.println("coding...");
}
}
2.3. 自定义
Handler
Handler
自定义一个
Handler
,实现
InvocationHandler
接口,通过重写内部的
invoke()
方法实现逻辑增强
public class WorkHandler implements InvocationHandler {
private final Object target;
public WorkHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("work")) {
System.out.println("before work...");
Object result = method.invoke(target, args);
System.out.println("after work...");
return result;
}
return method.invoke(target, args);
}
}
2.4. 测试
在
main()
方法中进行测试,使用
Proxy
类的静态方法
newProxyInstance()
生成一个代理对象并调用方法
public class MainTest {
public static void main(String[] args) {
Programmer programmer = new Programmer();
Worker worker = (Worker) Proxy.newProxyInstance(
programmer.getClass().getClassLoader(),
programmer.getClass().getInterfaces(),
new WorkHandler(programmer));
worker.work();
}
}
2.5. 输出结果
before work...
coding...
after work...
3. 源码分析
既然是一个代理的过程,那么肯定存在原生对象和代理对象之分,下面我们查看源码中是如何动态的创建代理对象的过程。上面例子中,创建代理对象调用的是
Proxy
类的静态方法
newProxyInstance()
,查看一下源码
3.1.
newProxyInstance()
方法
newProxyInstance()
public class Proxy implements java.io.Serializable {
protected InvocationHandler h;
// 有参构造器,参数是 InvocationHandler
protected Proxy(InvocationHandler h) {
Objects.requireNonNull(h);
this.h = h;
}
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException {
// 如果h为空直接抛出空指针异常,之后所有的单纯的判断null并抛异常,都是此方法
Objects.requireNonNull(h);
// 拷贝类实现的所有接口
final Class<?>[] intfs = interfaces.clone();
// 获取当前系统安全接口
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
// Reflection.getCallerClass 返回调用该方法的方法的调用类;loader:接口的类加载器
// 进行包访问权限、类加载器权限等检查
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
// 查找或生成指定的代理类
Class<?> cl = getProxyClass0(loader, intfs);
// 用指定的调用处理程序调用它的构造函数
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
// 获取代理类的构造函数对象
// constructorParams是类常量,作为代理类构造函数的参数类型,常量定义如下:
// private static final Class<?>[] constructorParams = { InvocationHandler.class };
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
// 根据代理类的构造函数对象来创建需要返回的代理类对象
return cons.newInstance(new Object[]{h});
} // 省略 catch......
}
}
-
在
checkProxyAccess()
方法中,进行参数验证 -
在
getProxyClass0()
方法中,生成一个代理类
Class
或寻找已生成过的代理类的缓存
-
通过
getConstructor()
方法获取生成的代理类的构造方法 -
通过
newInstance()
方法,生成最终的代理对象
上面这个过程中,
获取构造方法和生成代理对象都是利用的
Java
中的反射机制
,而需要重点看的是生成代理类的方法
getProxyClass0()
3.1.1.
getProxyClass0()
方法
getProxyClass0()
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
// 接口数不得超过 65535 个,这么大,足够使用的了
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// 如果缓存中有代理类了直接返回,否则将由代理类工厂ProxyClassFactory创建代理类
return proxyClassCache.get(loader, interfaces);
}
如果缓存中已经存在了就直接从缓存中取,这里的
proxyClassCache
是一个
WeakCache
类型,如果缓存中目标
classLoader
和接口数组对应的类已经存在,那么返回缓存的副本。如果没有就使用
ProxyClassFactory
去生成
Class
对象
3.1.1.1.
get()
方法
get()
// key:类加载器;parameter:接口数组
public V get(K key, P parameter) {
// 检查指定类型的对象引用不为空null。当参数为null时,抛出空指针异常
Objects.requireNonNull(parameter);
// 清除已经被 GC 回收的弱引用
expungeStaleEntries();
// 将ClassLoader包装成CacheKey, 作为一级缓存的key
Object cacheKey = CacheKey.valueOf(key, refQueue);
// 获取得到二级缓存
ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
// 没有获取到对应的值
if (valuesMap == null) {
ConcurrentMap<Object, Supplier<V>> oldValuesMap
= map.putIfAbsent(cacheKey,
valuesMap = new ConcurrentHashMap<>());
if (oldValuesMap != null) {
valuesMap = oldValuesMap;
}
}
// 根据代理类实现的接口数组来生成二级缓存key
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
// 通过subKey获取二级缓存值
Supplier<V> supplier = valuesMap.get(subKey);
Factory factory = null;
// 这个循环提供了轮询机制, 如果条件为假就继续重试直到条件为真为止
while (true) {
if (supplier != null) {
// 在这里supplier可能是一个Factory也可能会是一个CacheValue
// 在这里不作判断, 而是在Supplier实现类的get方法里面进行验证
V value = supplier.get();
if (value != null) {
return value;
}
}
if (factory == null) {
factory = new Factory(key, parameter, subKey, valuesMap);
}
if (supplier == null) {
// 到这里表明subKey没有对应的值, 就将factory作为subKey的值放入
supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) {
supplier = factory;
}
// 否则, 可能期间有其他线程修改了值, 那么就不再继续给subKey赋值, 而是取出来直接用
} else {
// 期间可能其他线程修改了值, 那么就将原先的值替换
if (valuesMap.replace(subKey, supplier, factory)) {
supplier = factory;
} else {
supplier = valuesMap.get(subKey);
}
}
}
}
很明显,重点关注下面代码
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
3.1.1.1.1.
apply()
方法
apply()
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>> {
// 代理类的前缀名都以 $Proxy 开始
private static final String proxyClassNamePrefix = "$Proxy";
// 使用唯一的编号给作为代理类名的一部分,如 $Proxy0,$Proxy1 等
private static final AtomicLong nextUniqueNumber = new AtomicLong();
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces) {
// 验证指定的类加载器(loader)加载接口所得到的Class对象(interfaceClass)是否与intf对象相同
Class<?> interfaceClass = null;
try {
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}
// 验证该Class对象是不是接口
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
// 验证该接口是否重复
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
// 声明代理类所在包
String proxyPkg = null;
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
// 验证所有非公共的接口在同一个包内;公共的就无需处理
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL;
String name = intf.getName();
int n = name.lastIndexOf('.');
// 截取完整包名
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}
// 1.根据规则生成文件名
if (proxyPkg == null) {
/*如果都是public接口,那么生成的代理类就在com.sun.proxy包下如果报 java.io.FileNotFoundException: com\sun\proxy\$Proxy0.class
(系统找不到指定的路径。)的错误,就先在你项目中创建com.sun.proxy路径*/
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
long num = nextUniqueNumber.getAndIncrement();
// 代理类的完全限定类名,如 com.sun.proxy.$Proxy0.calss
String proxyName = proxyPkg + proxyClassNamePrefix + num;
// 2.生成代理的字节码数组
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
// 3.生成 Class
try {
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
throw new IllegalArgumentException(e.toString());
}
}
}
在
apply()
方法中,主要做了下面
3
件事
- 根据规则生成文件名
-
利用
ProxyGenerator.generateProxyClass()
方法生成代理的字节码数组 -
调用方法
defineClass0()
生成
Class
3.1.2.
getConstructor()
和
newInstance()
方法
getConstructor()
newInstance()
返回代理类的
Class
后的流程,
获取构造方法和生成代理对象都是利用的
Java
中的反射机制
4. 代理对象长啥样
4.1. 代理对象长啥样
创建代理对象流程的源码分析完了,我们可以先通过
debug
来看看上面生成的这个代理对象究竟是个什么
和源码中看到的规则一样,是一个
Class
为
$Proxy0
的对象。再看一下代理对象的
Class
的详细信息
类的全限定类名是
com.sun.proxy.$Proxy0
,在上面我们提到过,这个类是在运行过程中动态生成的
4.2.
$Proxy0
反编译
$Proxy0
看一下反编译后
$Proxy0.java
文件的内容,下面的代码中,我只保留了核心部分,省略了无关紧要的
equals()、toString()、hashCode()
方法的定义
public final class $Proxy0 extends Proxy implements Worker{
public $Proxy0(InvocationHandler invocationhandler){
super(invocationhandler);
}
public final void work(){
try{
super.h.invoke(this, m3, null);
return;
}catch(Error _ex) { }
catch(Throwable throwable){
throw new UndeclaredThrowableException(throwable);
}
}
private static Method m3;
static {
try{
m3 = Class.forName("com.hydra.test.Worker").getMethod("work", new Class[0]);
//省略其他Method
}//省略catch
}
}
这个临时生成的代理类
$Proxy0
中主要做了下面的几件事
-
在这个类的静态代码块中,通过
反射机制
初始化了多个静态方法
Method
变量,除了接口中的方法还有
equals()、toString()、hashCode()
这三个方法 -
代理类
$Proxy0
继承了父类
Proxy
,在其实例化的过程中会调用父类的构造器,而父类
Proxy
中的构造器中传入的
InvocationHandler
对象实际上是我们自定义的
WorkHandler
的实例。此时就可以调用
WorkHandler
的
invoke()
方法了
-
同时,代理类
$Proxy0
也实现了自定义的接口
Worker
,并重写了
work()
方法,在
work()
方法内又调用了
InvocationHandler
的
invoke()
方法,也就是实际上调用了
WorkHandler
的
invoke()
方法
到这里,整体的流程就分析完了,我们可以用一张图来简要总结上面的过程
5.
JDK
动态代理为什么要有接口
JDK
其实如果不看上面的分析,我们也应该知道,
要扩展一个类有常见的两种方式,继承父类或实现接口
。这两种方式都允许我们对方法的逻辑进行增强,但现在不是由我们自己来重写方法,而是要想办法让
JVM
去调用
InvocationHandler
中的
invoke()
方法,也就是说代理类需要和两个东西关联在一起
- 被代理类
-
InvocationHandler
而
JDK
动态代理处理这个问题的方式是选择继承父类
Proxy
,并把
InvocationHandler
保存在父类的对象中
public class Proxy implements java.io.Serializable {
protected InvocationHandler h;
protected Proxy(InvocationHandler h) {
Objects.requireNonNull(h);
this.h = h;
}
// ......
}
通过父类
Proxy
的构造方法,保存了创建代理对象过程中传进来的
InvocationHandler
的实例
,使用
protected
修饰保证了它可以在子类中被访问和使用。但是同时,
因为
Java
是单继承的,因此在代理类
$Proxy0
继承了
Proxy
后,其只能通过实现目标接口的方式来实现方法的扩展
,达到我们增强目标方法逻辑的目的