JDK动态代理源码解析——ProxyWeakCacheProxyGenerator

Posted rotk2015

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JDK动态代理源码解析——ProxyWeakCacheProxyGenerator相关的知识,希望对你有一定的参考价值。

  1. JDK版本为RedHat OpenJDK 1.8.0_282

  2. JDK中的动态代理Proxy类仅限于方法拦截,且只能代理实现了接口的对象

  3. 调用Proxy的静态方法创建——继承了Proxy类的代理类(java只支持单继承,这就是为什么要求被代理类必须实现接口),通过该代理类的构造器创建对应代理对象,该对象实现了被代理类的接口,仅起到接口的作用。

  4. 代理类对象内持有一个——实现了InvocationHandler接口的对象(我们称它为中间对象吧)的引用,中间对象又持有一个被代理对象的引用。中间对象是真正办事的对象,对代理类对象的所有方法调用都会转发给中间对象,由中间对象的invoke方法实现,而中间对象的 invoke 方法,通过反射调用了被代理对象的对应方法

  5. 以上是从整体的角度介绍,下面从介绍下具体代码

    interface Animal
        public void speak();
    
    class Dog implements Animal
        @Override
        public void speak() 
            System.out.println("I am a dog.");
        
    
    class DogHandler implements InvocationHandler
        private Object obj;
        public dogHandler(Object obj)
            this.obj = obj;
        
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 
            System.out.println("In a handler.");
            method.invoke(obj, args);
            System.out.println(obj.toString());
            return proxy;
        
    
    public class test 
        public static void main(String[] args) throws IllegalAccessException,
                InstantiationException, NoSuchMethodException, InvocationTargetException 
            Dog aDog = new Dog();
            DogHandler h = new DogHandler(aDog);
    
    //        Animal pxyDog = (Animal) Proxy.newProxyInstance(dog.class.getClassLoader(),
    //                                                dog.class.getInterfaces(), h);
    
            Class pxyDogClazz = Proxy.getProxyClass(Dog.class.getClassLoader(), Dog.class.getInterfaces());
            Constructor pxyDogCons = pxyDogClazz.getConstructor(InvocationHandler.class);
            Animal pxyDog = (Animal) pxyDogCons.newInstance(h);
    
            pxyDog.speak();
        
    
    
  6. Proxy提供了两种方式创建代理对象,getProxyClass()创建代理类 + getConstructor() 反射获取构造器对象 + newInstance()创建实例,以及将其与对应对应异常处理代码打包在一块的,newProxyInstance()。

  7. getProxyClass() 接受类加载器与接口类型,实质是在内部调用getProxyClass0(),而后者则是向内部成员 proxyClassCache 申请,proxyClassCache 是一个缓存,如果之前创建过对应的代理类,则直接返回,否则创建。

    public static Class<?> getProxyClass(ClassLoader loader,
                                         Class<?>... interfaces)
        throws IllegalArgumentException
    
        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) 
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        
    
        return getProxyClass0(loader, intfs);
    
    /**
     1. Generate a proxy class.  Must call the checkProxyAccess method
     2. to perform permission checks before calling this.
     */
    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);
    
    /**
     * a cache of proxy classes
     */
    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
    
  8. 注意到,调用 proxyClassCache.get(loader, interfaces) 返回代理类,而 proxyClassCache 是 WeakCache 类,在构造时传入了 KeyFactoryProxyClassFactory 对象(均为Proxy的静态私有类)。阅读源码可发现,WeakCache 类中,KeyFactory 被 subKeyFactory 引用,ProxyClassFactory 被 valueFactory 引用。

    /**
     * Construct an instance of @code WeakCache
     *
     * @param subKeyFactory a function mapping a pair of
     *                      @code (key, parameter) -> sub-key
     * @param valueFactory  a function mapping a pair of
     *                      @code (key, parameter) -> value
     * @throws NullPointerException if @code subKeyFactory or
     *                              @code valueFactory is null.
     */
    public WeakCache(BiFunction<K, P, ?> subKeyFactory,
                     BiFunction<K, P, V> valueFactory) 
        this.subKeyFactory = Objects.requireNonNull(subKeyFactory);
        this.valueFactory = Objects.requireNonNull(valueFactory);
    
    
  9. 考虑到 WeakCache 类源码有点复杂,下面不贴代码,仅用文字描述其逻辑,感兴趣的读者可去看看具体代码。

  10. 当我们调用 proxyClassCache.get(loader, interfaces)时, 发生了什么?

  11. 首先,将 loader(类加载器)作为 key,生成 cacheKey,在 map(ConcurrentMap)中获取对应值 valuesMap(也是ConcurrentMap),这一步实际上就是在找类加载器对应的那个哈希图。

  12. 然后,利用 loader、interfaces(接口类型)通过 subKeyFactory(也就是KeyFactory) 生成 subKey,利用 subKey 在 valuesMap 中获取对应工厂 supplier。

  13. 紧接着,调用 supplier.get() 即可生成对应代理类。这里的 subpplier 是接口引用,引用的是 Factory 对象。而 Factory 是 WeakCache 的私有静态类,重写了 get() 方法,使得在 get() 内部,将 loader、interfaces 传递给 WeakCache 的 valueFactory(也就是ProxyClassFactory) 的 apply() 方法,并返回其返回值。

  14. 终于到了这一步,可以看出,我们的代理类是由这个 ProxyClassFactory 负责生成的。而 ProxyClassFactory 具体干了这么些事:定义代理类名称 proxyName($ProxyN,N为生成代理类的序号,0始),调用 ProxyGenerator 的 generateProxyClass() 生成字节流 proxyClassFile,最后调用本地方法 defineClass0() 将 proxyClassFile 还原成Class

    /**
     * A factory function that generates, defines and returns the proxy class given
     * the ClassLoader and array of interfaces.
     */
    private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>
    
        // prefix for all proxy class names
        private static final String proxyClassNamePrefix = "$Proxy";
    
        // next number to use for generation of unique proxy class names
        private static final AtomicLong nextUniqueNumber = new AtomicLong();
    
        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) 
        
        
        	//_____SOME CODES ELSE_____
    	    
    	    /*
    	     * Choose a name for the proxy class to generate.
    	     */
    	    long num = nextUniqueNumber.getAndIncrement();
    	    String proxyName = proxyPkg + proxyClassNamePrefix + num;
    	
    	    /*
    	     * Generate the specified proxy class.
    	     */
    	    byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
    	        proxyName, interfaces, accessFlags);
    	    try 
    	        return defineClass0(loader, proxyName,
    	                            proxyClassFile, 0, proxyClassFile.length);
    	     catch (ClassFormatError e) 
    	        /*
    	         * A ClassFormatError here means that (barring bugs in the
    	         * proxy class generation code) there was some other
    	         * invalid aspect of the arguments supplied to the proxy
    	         * class creation (such as virtual machine limitations
    	         * exceeded).
    	         */
    	        throw new IllegalArgumentException(e.toString());
    	    
        
    
    
  15. ProxyGenerator 位于 package sun.misc 内,JDK文档查不到对应资料。ProxyGenerator 内的 generateProxyClass() 调用 generateClassFile() 生成字节流文件。在生成代理类的字节流的过程中,除了添加接口的方法外,还调用 generateConstructor() 生成并添加代理类的构造器方法(这就是之前 getConstructor() 反射获取的),调用 generateStaticInitializer() 生成并添加静态初始化方法,以及hashCode()、equals()、toString()

    另:在生成代理类的构造器时,添加了父类——即 Proxy 的构造器(相当于 super()),而在 Proxy 类中,有两个 protected 成员:

    /**
     * the invocation handler for this proxy instance.
     * @serial
     */
    protected InvocationHandler h;
    /**
     * Constructs a new @code Proxy instance from a subclass
     * (typically, a dynamic proxy class) with the specified value
     * for its invocation handler.
     *
     * @param  h the invocation handler for this proxy instance
     *
     * @throws NullPointerException if the given invocation handler, @code h,
     *         is @code null.
     */
    protected Proxy(InvocationHandler h) 
        Objects.requireNonNull(h);
        this.h = h;
    
    
  16. 说了这么多,这个生成的代理类究竟是什么样子呢?我们在main函数开始加上如下代码,然后运行main函数即可在项目下生成并保存 $Proxy0.class 文件,将其拖到IDEA中会自动反编译出对应.java文件。

    System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
    
  17. $Proxy0代码:

    //
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by Fernflower decompiler)
    //
    
    package myBoard;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.UndeclaredThrowableException;
    
    final class $Proxy0 extends Proxy implements Animal 
        private static Method m1;
        private static Method m2;
        private static Method m3;
        private static Method m0;
    
        public $Proxy0(InvocationHandler var1) throws  
            super(var1);
        
    
        public final boolean equals(Object var1) throws  
            try 
                return (Boolean)super.h.invoke(this, m1, new Object[]var1);
             catch (RuntimeException | Error var3) 
                throw var3;
             catch (Throwable var4) 
                throw new UndeclaredThrowableException(var4);
            
        
    
        public final String toString() throws  
            try 
                return (String)super.h.invoke(this, m2, (Object[])null);
             catch (RuntimeException | Error var2) 
                throw var2;
             catch (Throwable var3) 
                throw new UndeclaredThrowableException(var3);
            
        
    
        public final void speak() throws  
            try 
                super.h.invoke(this, m3, (Object[])null);
             catch (RuntimeException | Error var2) 
                throw var2;
             catch (Throwable var3) 
                throw new UndeclaredThrowableException(var3);
            
        
    
        public final int hashCode() throws  
            try 
                return (Integer)super.h.invoke(this, m0, (Object[])null);
             catch (RuntimeException | Error var2) 
                throw var2;
             catch (Throwable var3) 
                throw new UndeclaredThrowableException(var3);
            
        
    
        static 
            try 
                m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
                m2 = Class.forName("java.lang.Object").getMethod("toString");
                m3 = Class.forName("myBoard.Animal").getMethod("speak");
                m0 = Class.forName("java.lang.Object").getMethod("hashCode");
             catch (NoSuchMethodException var2) 
                throw new NoSuchMethodError(var2.getMessage());
             catch (ClassNotFoundException var3) 
                throw new NoClassDefFoundError(var3.getMessage());
            
        
    
    
    
  18. 可以看到,
    1.$Proxy0构造函数的实现完全依托于父类Proxy(详见第14条)
    2.内部实现除了接口方法外,也如之前所说实现了hashCode()、equals()、toString();
    3.每个方法的实现方式都是 super.h.invoke(this,mN,…),即调用继承自父类的 protected 成员 h(再见第14条,首 尾 呼 应)
    4.传递给h.invoke()的,除了必要的方法信息,还有本代理类对象的引用 this,这就解释了 InvocationHandler 的 invoke 方法的第一个形参 Object proxy 到底是什么。

参考资料:

  1. JDK动态代理详解(动态代理类源码解析

以上是关于JDK动态代理源码解析——ProxyWeakCacheProxyGenerator的主要内容,如果未能解决你的问题,请参考以下文章

Java:Jdk动态代理技术解析

Java:Jdk动态代理技术解析

追踪解析 jdk Proxy 源码

JDK动态代理源码

JDK动态代理源码

JDK动态代理源码