动态代理最全详解系列[2]-Proxy生成代理类对象源码分析

  之前我们通过JDK中的Proxy实现了动态代理,Proxy用起来是比较简便的,但理解起来不是那么清晰,是因为我们并没有看见代理类是怎么生成的,代理类怎么调用的被代理类方法,所以下面我们进入源码看一下。

源码分析

首先进入创建代理对象的方法Proxy.newProxyInstance()中:

@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
    throws IllegalArgumentException
{
    //InvocationHandler对象非空校验
    Objects.requireNonNull(h);

    //复制代理类实现的所有接口
    final Class<?>[] intfs = interfaces.clone();
    //获取安全管理器
    final SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        //一些权限校验
        checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
    }

    //---------------------此处是核心---------------------
    //【核心】获取代理类,先尝试从缓存获取,没有的话再生成
    Class<?> cl = getProxyClass0(loader, intfs);

    //调用代理类构造器生成代理对象
    try {
        if (sm != null) {
            //一些权限校验
            checkNewProxyPermission(Reflection.getCallerClass(), cl);
        }

        //获取代理类的构造器,该构造器参数类型为InvocationHandler.class
        //constructorParams是Proxy类的一个静态常量,值为{ 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;
                }
            });
        }
        //用上面获取的代理类构造器创建一个代理类对象并返回,参数为传入的InvocationHandler对象
        return cons.newInstance(new Object[]{h});
    } catch (IllegalAccessException|InstantiationException e) {
        throw new InternalError(e.toString(), e);
    } catch (InvocationTargetException e) {
        Throwable t = e.getCause();
        if (t instanceof RuntimeException) {
            throw (RuntimeException) t;
        } else {
            throw new InternalError(t.toString(), t);
        }
    } catch (NoSuchMethodException e) {
        throw new InternalError(e.toString(), e);
    }
}

Proxy.newProxyInstance()方法做的事情已经在注释中说明了,其中最重要的一行代码就是获取代理类Class cl = getProxyClass0(loader, intfs),我们进入这个方法看一下:

private static Class<?> getProxyClass0(ClassLoader loader,
                                       Class<?>... interfaces) {
    if (interfaces.length > 65535) {
        throw new IllegalArgumentException("interface limit exceeded");
    }

    //如果实现了接口interfaces的代理类已经被类加载器loader加载过了,那就
    //将缓存中的代理类返回,否则,通过ProxyClassFactory生成代理类
    return proxyClassCache.get(loader, interfaces);
}

proxyClassCache.get()会尝试从缓存寻找代理类,找不到的话就调用ProxyClassFactory的apply()生成代理类,我们看一下ProxyClassFactory里是怎么生成代理类的:

private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
    //定义所有代理类名字前缀为"$Proxy"
    private static final String proxyClassNamePrefix = "$Proxy";

    //代理类名字为"$Proxy"+序号,用原子类来生成代理类的序号,确保代理类名字唯一
    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) {
            //验证intf是否是由指定的类加载器loader加载的
            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");
            }
            //验证intf是否是接口
            if (!interfaceClass.isInterface()) {
                throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
            }
            //验证intf在interfaces中是否有重复
            if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                throw new IllegalArgumentException(
                        "repeated interface: " + interfaceClass.getName());
            }
        }

        //代理类的包名
        String proxyPkg = null;
        //代理类访问标志,默认是public final
        int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

        //如果接口有一个是非public的,就设置代理类包名与接口包名相同
        for (Class<?> intf : interfaces) {
            int flags = intf.getModifiers();
            if (!Modifier.isPublic(flags)) {
                //更改代理类访问标志为final
                accessFlags = Modifier.FINAL;
                //获取接口的全限定名
                String name = intf.getName();
                int n = name.lastIndexOf('.');
                //如果接口全限定名中除去接口名后的包名
                String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                if (proxyPkg == null) {
                    //第一次遇到非public的接口,将代理类包名设置与接口包名相同
                    proxyPkg = pkg;
                } else if (!pkg.equals(proxyPkg)) {
                    //如果代理类实现了不同包的非public接口,这里会报错
                    throw new IllegalArgumentException(
                            "non-public interfaces from different packages");
                }
            }
        }

        if (proxyPkg == null) {
            //如果接口全是public的,代理类放在默认包下:com.sun.proxy
            proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
        }

        //生成代理类的序号
        long num = nextUniqueNumber.getAndIncrement();
        //生成代理类的全限定名:包名+"$Proxy"+序号
        String proxyName = proxyPkg + proxyClassNamePrefix + num;

        //---------------------此处是核心---------------------
        //【核心】生成代理类的字节码
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
        try {
            //根据字节码文件生成Class对象即代理类
            return defineClass0(loader, proxyName,
                    proxyClassFile, 0, proxyClassFile.length);
        } catch (ClassFormatError e) {
            throw new IllegalArgumentException(e.toString());
        }
    }
}

通过上面代码看到ProxyClassFactory主要是确定了代理类的全限定名、要实现的接口、访问标志,然后调用ProxyGenerator类的generateProxyClass()方法来生成代理类的字节码文件,根据字节码文件获取代理类,具体字节码文件怎么生成的我们留到下一篇专门介绍,这里我们重点关注下生成的代理类是什么样的,为了一睹代理类的真容,我们直接调用generateProxyClass()方法生成代理类的字节码并写入到文件中:

byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", FontProviderFromDisk.class.getInterfaces());
String path = "D:\\ideaProject\\remote\\designpattern\\src\\com\\codezhao\\designpattern\\proxypattern\\dynamicproxy\\FontProviderProxy.class";
try(FileOutputStream fos = new FileOutputStream(path)) {
    fos.write(classFile);
    fos.flush();
    System.out.println("代理类class文件写入成功");
} catch (Exception e) {
    System.out.println("写文件错误");
}

打开我们生成的代理类$Proxy0,看看里面到底有什么:

import com.codezhao.designpattern.proxypattern.FontProvider;
import java.awt.Font;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements FontProvider {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    //代理类的构造器需要传递InvocationHandler类型的参数,所以我们大概就可以猜到
    //为什么代理对象调用方法都是执行了InvocationHandler对象的invoke()方法

    //这里的super()调用了父类Proxy的构造器:
    //protected Proxy(InvocationHandler h) {
    //        Objects.requireNonNull(h);
    //        this.h = h;
    //    }
    //所以代理类中有个h属性即InvocationHandler对象
    public $Proxy1(InvocationHandler var1) throws  {
        super(var1);
    }

    //调用代理类的getFont()方法,直接调用了h即InvocationHandler对象的invoke()方法
    //所以就清楚了,代理对象关联了InvocationHandler对象,InvocationHandler对象关联了
    //被代理对象。InvocationHandler对象就像一个中介,代理对象通过它间接地调用被代理对象
    //的方法,这个中介还可以完成一些额外的工作,我们的例子中就是缓存功能。

    public final Font getFont(String var1) throws  {
        try {
            return (Font)super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    static {
        //这个静态代码块中通过反射获得了getFont方法
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("com.codezhao.designpattern.proxypattern.FontProvider").getMethod("getFont", Class.forName("java.lang.String"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
    //为节省篇幅,这里将equals()、toString()、hashCode()省略了
}

在代理类$Proxy0的代码中,我们可以看到它是如何调用的被代理类,上面的注释写的很清楚了。


从最初的Proxy.newProxyInstance()走到这里,我们应该知道了Proxy.newProxyInstance()的功能就是生成了代理类$Proxy0的对象,我们直接用这个代理类$Proxy0做测试,结果同调用Proxy.newProxyInstance()的效果相同。

public class CachedProviderHandlerTest {
    public static void main(String[] args) {
        FontProvider fontProvider = new FontProviderFromDisk();
        CachedProviderHandler cachedProviderHandler = new CachedProviderHandler(fontProvider);
        
        //生成代理对象,直接new一个我们生成的代理类$Proxy0,不用Proxy.newProxyInstance()
        $Proxy0 fontProviderProxy = new $Proxy0(cachedProviderHandler);
        
        fontProviderProxy.getFont("xxx");
    }
}

另外想要保存代理类class文件的话,还可以通过在测试类中加上下面这行代码来实现,生成的代理类会保存到com.sun.proxy包下,这个包就是上面ProxyClassFactory源码分析中看到的代理类保存的默认包名。

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");


总结一下动态代理底层实现的流程:

1.根据代理类的类加载器和要实现的接口生成代理类;

2.获取构造器参数类型为InvocationHandler.class的代理类构造器;

3.根据代理类构造器和传入的InvocationHandler对象创建代理类对象;

其中第1步又做了这些事:

1.确定代理类的全限定名、要实现的接口、访问标志;

2.生成代理类的字节码文件;

3.根据代理类的字节码文件获取代理类;

具体代理类的字节码文件是怎么生成的,我们留到下一篇专门介绍。

一些思考

1.InvocationHandler就像一个中介,代理对象中关联了InvocationHandler对象,InvocationHandler对象中关联了被代理对象。代理对象调用方法时直接调用InvocationHandler对象的invoke()方法,在InvocationHandler的invoke()方法中再调用被代理对象的方法,同时在invoke()中可以做一些额外的工作,我们的例子中就是缓存功能;

2.代理类继承了Proxy类,由于Java只支持单继承,所以Proxy+InvocationHandler方式只能针对接口做代理;

3.动态代理的实现依赖于反射机制,而Spring的AOP就是通过代理实现的,所以反射机制对于Spring框架是至关重要的,我们再一次体会到了反射的强大;

4.Spring AOP中对接口的代理其实就是基于Proxy+InvocationHandler实现的,而对类的代理是通过Cglib;


下一篇我们分析ProxyGenerator是如何生成代理类的字节码文件。
欢迎关注我的公众号【codeZhao】,获取更多技术干货和职业思考。

你可能感兴趣的:(java,设计模式,jdk动态代理,源码)