动态代理原理
我们在上篇文章中提到通过调用Proxy
类的newProxyInstance()
方法生成了一个临时实例,这个实例就是我们需要的代理类。我们的代理类被动态的创建,省去了我们针对业务方法而手动实现如日志记录等功能。
我们继续深入,来详细看一下这个过程:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,
InvocationHandler h) {
...
//1. 获取传入的之前被代理类实现的接口
final Class<?>[] intfs = interfaces.clone();
...
//2. 生成设计好的代理类的Class对象
Class<?> cl = getProxyClass0(loader, intfs);
...
//3. 获取生成好代理类的带参构造方法,其中参数就是InvocationHandler接口的实例
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
...
//4. 通过构造方法生成代理类实例
return cons.newInstance(new Object[]{h});
...
}
这就是Proxy
类newProxyInstance
方法的核心逻辑,其中去除了一些判断和异常处理代码,这些都跟我们目前的讨论没有太大关系。
可以看出来,这里最重要的就是这一句:
//2. 生成设计好的代理类的Class对象
Class<?> cl = getProxyClass0(loader, intfs);
生成了设计好的临时代理类的Class
对象之后,我们可以使用反射获取它的构造方法进而生成它的实例,这个Class
对象的具体生成方法是什么呢?
我们继续探究它的实现方式:
/**
* 生成代理类的Class对象
*/
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
...
// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
//使用缓存获取之前生成的Class对象,如果之前未生成,则使用ProxyClassFactory生成
return proxyClassCache.get(loader, interfaces);
}
这里使用到了缓存技术,如果之前生成过该类的Class对象,直接通过缓存返回它。我们这是第一次生成,看注释中提到:
If the proxy class defined by the given loader implementing
the given interfaces exists, this will simply return the cached copy;
otherwise, it will create the proxy class via the ProxyClassFactory
意思是之前已经创建过这个代理类,那么直接使用缓存,未建立的话使用ProxyClassFactory
类建立代理类,通过缓存类的代码,我们发现其中调用了ProxyClassFactory
的apply
方法。我们现在做的就是研究代理类的生成机理,所以来看看ProxyClassFactory
。
我使用的是eclipse
,使用快捷键:Ctrl+Shift+T
打开搜索界面,输入ProxyClassFactory
,找到这个类。这个类其实是Proxy
类 的一个内部类,我们通过eclipse
发现其中只有一个方法apply
,看来就是它了,它的代码是:
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
...
//1. 生成指定的代理类的二进制数组
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
...
//2. 通过生成的二进制数组proxyClassFile生成一个Class<?>类型的实例,这是一个native方法
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
}
和之前一样,略去了一些判断语句和生成细节等代码。
我们发现直接通过ProxyGenerator
类的generateProxyClass
方法得到了一个二进制文件,这个也就是我们平时说的.class
文件。到此处已经比较清楚代理类的生成过程了,之后二进制文件的具体生成步骤不再跟进,有兴趣的读者可以继续深入研究。
最后根据这个.class
文件通过虚拟机加载,解析,初始化生成代理类的Class
类对象,最终,使用反射获取其构造方法后生成代理类的对象。
我们通过下面的图看可能会清晰一点:
其实用户接触的只有一个newProxyInstance
方法,其余的方法都是层层调用被使用的。其实就具体就干了两件事:生成代理类Class
对象和生成代理类对象,后者依赖于前者。
按照图中标注的顺序而且根据方法具体的行为来说:
a. 外部测试代码调用Proxy
类的newProxyInstance
方法
b. newProxyInstance
方法中调用了Proxy
类的getProxyClass0
方法
c. getProxyClass0
方法中调用Proxy
类的内部类ProxyClassFactory
类的apply
方法
d. ProxyClassFactory
类的apply
方法中调用ProxyGenerator
类的generateProxyClass
方法,这步生成了一个二进制数组
e. generateProxyClass
方法把二进制数组返回给apply
方法
f. 之后apply
方法继续调用native
方法defineClass0
,然后生成了一个Class
类对象
g. Class
对象返回给apply
方法
h. Class
对象返回给getProxyClass0
方法
i. Class
对象返回给newProxyInstance
方法
j. newProxyInstance
方法中使用返回的Class
对象,通过反射获取其带参构造方法,之后生成一个代理类的对象,返回给测试类
具体行为对应的方法:
参考文章
[1] Java动态代理机制详解(JDK 和CGLIB,Javassist,ASM)
[2] Java之美[从菜鸟到高手演练]之JDK动态代理的实现及原理)
[3] JAVA 动态代理