美文网首页
源码角度理解JDK动态代理的实现原理

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

作者: 挡不住的柳Willow | 来源:发表于2017-11-14 16:36 被阅读13次

    在另一篇文章浅析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(数字标识不同的类,如$Proxy1,$Proxy2)
    • 2、通过ProxyGenerator.generateProxyClass()方法生成代理类的二进制文件
    • 3、通过defineClass0()方法产生对应名称的Class对象并返回
      因此ProxyClassFactory中我们主要关注生成二进制文件的ProxyGenerator.generateProxyClass()方法:
    //传入代理类名proxyName->var0,接口对应的Class对象interfaces->var1,标识接口是否为共有接口的accessFlags->var2
    public 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动态代理实现原理

    相关文章

      网友评论

          本文标题:源码角度理解JDK动态代理的实现原理

          本文链接:https://www.haomeiwen.com/subject/cowzmxtx.html