Steve's Blog

Talk is cheap, show me the code.

0%

动态代理2

代理模式

动态代理原理

我们在上篇文章中提到通过调用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});   
            ...
    }

这就是ProxynewProxyInstance方法的核心逻辑,其中去除了一些判断和异常处理代码,这些都跟我们目前的讨论没有太大关系。
可以看出来,这里最重要的就是这一句:

    //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类建立代理类,通过缓存类的代码,我们发现其中调用了ProxyClassFactoryapply方法。我们现在做的就是研究代理类的生成机理,所以来看看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类对象,最终,使用反射获取其构造方法后生成代理类的对象。

我们通过下面的图看可能会清晰一点:

Proxy函数调用图

其实用户接触的只有一个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 动态代理