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

在另一篇文章浅析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() {
                    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[], Class>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

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

private static final class ProxyClassFactory
        implements BiFunction[], 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, 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() {
                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动态代理的实现原理)