源码角度理解JDK动态代理的实现原理

来源:互联网 发布:内眼角后遗症知乎 编辑:程序博客网 时间:2024/06/06 00:36

在另一篇文章浅析AOP实现原理(2)JDK动态代理中我们了解了JDK动态代理的用法,但是光会用还不行,这篇文章我们来探讨几个第一次使用JDK动态代理时可能都会产生的疑问:
* 1、代理类对象究竟是如何生成的
* 2、invoke方法是何时被调用的

JDK如何动态生成代理类对象

在上一篇文章中,生成代理的方法为:

public static Object getProxy(Object object){        return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(),                new ProxyClient(object));    }

我们来看看这个方法是如何实现的(以下非完整代码,只展示了部分重要代码)

//这个方法中传入了代理对象的类加载器,接口和相应的InvocationHandler对象(最终调用者触发其中的Invoke方法):public static Object newProxyInstance(ClassLoader loader,                                          Class<?>[] interfaces,                                          InvocationHandler )throws IllegalArgumentException    {      //复制接口        final Class<?>[] intfs = interfaces.clone();      //生成代理类        Class<?> cl = getProxyClass0(loader, intfs);     //根据构造函数来创建代理对象            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});    }

由此可知newProxyInstance函数做了两件事:生成代理类的Class对象,然后用Class对象的构造函数创建代理对象,我们先来看看getProxyClass0方法是如何生成代理类的

private static Class<?> getProxyClass0(ClassLoader loader,                                           Class<?>... interfaces) {       //判断接口数是否超标        if (interfaces.length > 65535) {            throw new IllegalArgumentException("interface limit exceeded");        }        // 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        return proxyClassCache.get(loader, interfaces);    }

根据源码中的注释可知,JAVA使用WeakCache对代理类进行了缓存,该缓存的定义如下

 private static final WeakCache<ClassLoader, Class<?>[], Class<?>>        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

具体的缓存机制我们不去研究,只需要知道它将首先从缓存中查找是否存在,若未找到,会通过ProxyClassFactory来创建代理类。行,那我们就再来看看ProxyClassFactory的代码

private static final class ProxyClassFactory        implements BiFunction<ClassLoader, Class<?>[], Class<?>>    {        //所有代理类的前缀        private static final String proxyClassNamePrefix = "$Proxy";        //代理类类名后缀,是AtomicLong类型的唯一计数        private static final AtomicLong nextUniqueNumber = new AtomicLong();         //重写BiFunction接口的方法        @Override        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {          //创建一个IdentitiHashMap用来存放获取的类            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);            for (Class<?> intf : interfaces) {                Class<?> interfaceClass = null;            //根据接口的名称获取类               interfaceClass = Class.forName(intf.getName(), false, loader);            //利用IdentityHashMap的特性判断是否有重复的类                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");                    }                }            }            //默认包名为com.sun.proxy            if (proxyPkg == null) {                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";            }            //确定代理类名,默认为com.sun.proxy.$Proxy0(第二个代理类后缀为1,以此类推)            long num = nextUniqueNumber.getAndIncrement();            String proxyName = proxyPkg + proxyClassNamePrefix + num;            //生成代理类的二进制文件            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(                proxyName, interfaces, accessFlags);              //返回代理类                return defineClass0(loader, proxyName,                                    proxyClassFile, 0, proxyClassFile.length);        }    }

由此可知,ProxyClassFactory实现了BiFunction接口,将类加载器和接口数组传入重写的apply()方法中,其中主要做了三件事:
* 1、生成代理类的名称,默认为com.sun.proxy.$Proxy0(数字标识不同的类,如Proxy1Proxy2)
* 2、通过ProxyGenerator.generateProxyClass()方法生成代理类的二进制文件
* 3、通过defineClass0()方法产生对应名称的Class对象并返回
因此ProxyClassFactory中我们主要关注生成二进制文件的ProxyGenerator.generateProxyClass()方法:

//传入代理类名proxyName->var0,接口对应的Class对象interfaces->var1,标识接口是否为共有接口的accessFlags->var2public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {         //生成ProxyGenerator对象        ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);          //调用generateClassFile()方法生成类的二进制文件        final byte[] var4 = var3.generateClassFile();        //如果saveGeneratedFiled为true,将文件写入硬盘的IO操作        if (saveGeneratedFiles) {            AccessController.doPrivileged(new PrivilegedAction<Void>() {                public Void run() {                        int var1 = var0.lastIndexOf(46);                        Path var2;                        if (var1 > 0) {                            Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));                            Files.createDirectories(var3);                            var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");                        } else {                            var2 = Paths.get(var0 + ".class");                        }                        Files.write(var2, var4, new OpenOption[0]);                        return null;            });        }        return var4;    }

所以在ProxyGenerator.generateProxyClass()方法中,首先调用了var3.generateClassFile()方法产生二进制文件,然后将该文件写入硬盘或直接返回,那我们再来关注一下var3.generateClassFile()(我保证这是最后一次)

private byte[] generateClassFile() {         //将hashCode、equals、toString方法添加到proxyMethods这个map中        this.addProxyMethod(hashCodeMethod, Object.class);        this.addProxyMethod(equalsMethod, Object.class);        this.addProxyMethod(toStringMethod, Object.class);        //获取接口类和个数        Class[] var1 = this.interfaces;        int var2 = var1.length;        int var3;        Class var4;        //遍历所有的接口        for(var3 = 0; var3 < var2; ++var3) {            var4 = var1[var3];            //获得该接口中所有的方法            Method[] var5 = var4.getMethods();            int var6 = var5.length;            //将方法添加到代理方法            for(int var7 = 0; var7 < var6; ++var7) {                Method var8 = var5[var7];                this.addProxyMethod(var8, var4);            }        }        Iterator var11,        //中间省略验证步骤        Iterator var15;            //生成代理类的构造函数并添加到methods,methods是一个List,它包含了代理类的方法信息            this.methods.add(this.generateConstructor());            var11 = this.proxyMethods.values().iterator();            while(var11.hasNext()) {                var12 = (List)var11.next();                var15 = var12.iterator();          //将proxyMethod里的方法和变量存入fields和methods里                while(var15.hasNext()) {                    ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next();                    this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10));                    this.methods.add(var16.generateMethod());                }            }          //一些初始化            this.methods.add(this.generateStaticInitializer());          //后面主要是一些将methods和fields写成二进制流的文件操作             ·······          return .......toByteArray();        }    }

至此我们终于得到了代理类的二进制文件,再来理一理思路:
首先Proxy.newProxyInstance()方法被调用,在该方法中,getProxyClass()方法被调用,在getProxyClass方法中先查找缓存中是否有代理类,若没有,则调用ProxyClassFactoryapply()方法来产生代理类,apply()方法中先根据自定义的规则生成包名和类名进行拼接,然后调用ProxyGenerator.generateProxyClass()方法生成代理类的二进制文件,而真正生成二进制文件的方法是其中的var3.generateClassFile()方法,该方法中遍历所有接口,获得接口中的方法和变量信息,最后根据这些信息写成二进制文件,根据saveGeneratedFileds判断是否写入硬盘,二进制文件返回到ProxyClassFactoryapply()方法中,再根据包名,类加载器,代理类的二进制文件生成代理类并返回。
看起来有点混乱,其实用一句话来说就是:通过java反射从接口中获取变量和方法信息来生成代理类的过程

invoke方法是何时被调用的

要知道invoke何时被调用,就必须先知道jdk动态代理为我们生成的文件究竟是什么,以下代码在指定路径输出文件:

@Test    public void testProxy() throws IOException {        String path = "D:/$Proxy1.class";        byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy1", KillerImpl.class.getInterfaces());        FileOutputStream out = new FileOutputStream(path);            out.write(classFile);            out.flush();    }

到D盘下用反编译工具获得Proxy1的源文件(不会反编译的点这里):

import com.lwl.aop.Killer;import java.lang.reflect.*;public final class $Proxy1 extends Proxy    implements Killer{    //调用Proxy的构造函数注入invocationHandler    public $Proxy1(InvocationHandler invocationhandler)    {        super(invocationhandler);    }    public final boolean equals(Object obj)    {        try        {            return ((Boolean)super.h.invoke(this, m1, new Object[] {                obj            })).booleanValue();        }        catch(Error _ex) { }        catch(Throwable throwable)        {            throw new UndeclaredThrowableException(throwable);        }    }    public final String toString()    {        try        {            return (String)super.h.invoke(this, m2, null);        }        catch(Error _ex) { }        catch(Throwable throwable)        {            throw new UndeclaredThrowableException(throwable);        }    }    public final int hashCode()    {        try        {            return ((Integer)super.h.invoke(this, m0, null)).intValue();        }        catch(Error _ex) { }        catch(Throwable throwable)        {            throw new UndeclaredThrowableException(throwable);        }    }    //的kill方法在此处被调用    public final void kill()    {        try        {            //调用Proxy中invocationHandler的invoke方法            super.h.invoke(this, m3, null);            return;        }        catch(Error _ex) { }        catch(Throwable throwable)        {            throw new UndeclaredThrowableException(throwable);        }    }    private static Method m1;    private static Method m2;    private static Method m0;    private static Method m3;    static     {        try        {            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {                Class.forName("java.lang.Object")            });            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);            m3 = Class.forName("com.lwl.aop.Killer").getMethod("kill", new Class[0]);        }        catch(NoSuchMethodException nosuchmethodexception)        {            throw new NoSuchMethodError(nosuchmethodexception.getMessage());        }        catch(ClassNotFoundException classnotfoundexception)        {            throw new NoClassDefFoundError(classnotfoundexception.getMessage());        }    }}

这个文件是继承自Proxy并实现了Killer的类,注入了invocationHandler,包含equalstoStringhashCodekill四个方法,在各个方法内部调用ProxyInvocationHandler中的invoke方法,至此我们终于弄明白了JDK动态代理的实现原理

部分参考JDK动态代理实现原理

原创粉丝点击