zoukankan      html  css  js  c++  java
  • JDK动态代理实现机制

    ===========================================

        原文链接: JDK动态代理实现机制   转载请注明出处!

    ===========================================

    本文只对JDK动态代理的底层实现进行分析,如需了解代理模式和动态代理的使用请移步:设计模式—代理模式     动态代理的两种实现方式(JDK/Cglib)

    在读代码之前先清楚两个概念:

      1、Class类存储的是类的所有信息,包括类的所有方法、属性、实现接口等。每个类对应一个Class对象(单例),Class对象是由classLoader加载出来的,使用双亲委派模型来保证class只会被加载一次。

      2、classLoader在加载类的时候不管class文件是从哪里来的,无论是从.class文件、网络、数据库类加载器都不关心。他只关心给他的class二进制流是不是能够通过校验。

    说明:以下测试代码和 动态代理的两种实现方式(JDK/Cglib)相同

    使用JDK动态代理需要实现InvocationHandler接口,同时实现invoke()方法。

    package com.zpj.proxy.jdk;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    /**
     * Created by Perkins on 2017/4/2.
     */
    public class JDKProxy implements InvocationHandler {
        private Object person;
        public Object getInstance(Object person) {
            this.person = person;
            return Proxy.newProxyInstance(person.getClass().getClassLoader(), person.getClass().getInterfaces(), this);
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("doSomething---------start");
            method.invoke(person, args);
            System.out.println("doSomething---------end");
            return null;
        }
    }

    测试代码如下:

    package com.zpj.proxy.jdk;
    
    /**
     * Created by Perkins on 2017/4/2.
     */
    public class Run {
    
        public static void main(String[] args) {
            Person person = (Person) new JDKProxy().getInstance(new MrLi());
            person.doWork();
        }
    }

    运行的时候在person处打断点可看到person的类名为$Proxy0而不是Person或者MrLi,则说明该返回对象是Person的实现类

    我们添加如下代码把person的class中的方法打印出来

    package com.zpj.proxy.jdk;
    
    /**
     * Created by Perkins on 2017/4/2.
     */
    import java.lang.reflect.Method;
    public class Run {
        private Method method;
        public static void main(String[] args) {
           Person person = (Person) new JDKProxy().getInstance(new MrLi());
           Method [] methods = person.getClass().getMethods();
           for(int i =0; i<methods.length;i++){
               System.out.println(methods[i].getName());
           }
            person.doWork();
        }
    } 

    结果如下,很明显红框中的方法不属于Person也不属于Object中的方法。这更进一步说明返回的person并不是Person的实例。

    下面就进入底层代码对JDK动态代理进行解析。

    这里直接对Proxy.newProxyInstance(person.getClass().getClassLoader(), person.getClass().getInterfaces(), this);进行分析。

    public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h)throws IllegalArgumentException{
            if (h == null) {
            //验证InvocationHandler不允许为null
            throw new NullPointerException();
            }
        /*
         * Look up or generate the designated proxy class.
         */
        //调用getProxyClass()获取Class实例,该实例便是返回的代理Person的实例,此方法为重点!!!
            Class cl = getProxyClass(loader, interfaces);
        /*
         * Invoke its constructor with the designated invocation handler.
         */
            try {
            //利用反射机制从Class中取出构造器创建对象
            Constructor cons = cl.getConstructor(constructorParams);
            return (Object) cons.newInstance(new Object[] { h });
            } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString());
            } catch (IllegalAccessException e) {
            throw new InternalError(e.toString());
            } catch (InstantiationException e) {
            throw new InternalError(e.toString());
            } catch (InvocationTargetException e) {
            throw new InternalError(e.toString());
            }
            }

     在上面方法中调用public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)throws IllegalArgumentException获取了Class实例,下面进入该方法进行分析。

    public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) throws IllegalArgumentException {
            if (interfaces.length > 65535) {
                //验证接口数量不允许超过65535
                throw new IllegalArgumentException("interface limit exceeded");
            }
            //************开始对interface进行循环验证,验证通过则加入interfaceNames中***************************
            Class proxyClass = null;
        /* collect interface names to use as key for proxy class cache */
            String[] interfaceNames = new String[interfaces.length];
            Set interfaceSet = new HashSet();    // for detecting duplicates
            for (int i = 0; i < interfaces.length; i++) {//循环对所有接口进行操作
            /*
             * Verify that the class loader resolves the name of this
             * interface to the same Class object.
             */
                String interfaceName = interfaces[i].getName();
                Class interfaceClass = null;
                try {
                    //根据名称获取接口的Class
                    interfaceClass = Class.forName(interfaceName, false, loader);
                } catch (ClassNotFoundException e) {
                }
                if (interfaceClass != interfaces[i]) {
                    throw new IllegalArgumentException(interfaces[i] + " is not visible from class loader");
                }
            /*
             * Verify that the Class object actually represents an
             * interface.
             */
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException( interfaceClass.getName() + " is not an interface");
                }
            /*
             * Verify that this interface is not a duplicate.
             */
                if (interfaceSet.contains(interfaceClass)) {
                    throw new IllegalArgumentException( "repeated interface: " + interfaceClass.getName());
                }
                interfaceSet.add(interfaceClass);
                interfaceNames[i] = interfaceName;
            }
    //************结束对interface进行循环验证,存储于interfaceNames中***************************
        /*
         * Using string representations of the proxy interfaces as
         * keys in the proxy class cache (instead of their Class
         * objects) is sufficient because we require the proxy
         * interfaces to be resolvable by name through the supplied
         * class loader, and it has the advantage that using a string
         * representation of a class makes for an implicit weak
         * reference to the class.
         */
            Object key = Arrays.asList(interfaceNames);
    
        /*
         * Find or create the proxy class cache for the class loader.
         */
            Map cache;
            synchronized (loaderToCache) {
                cache = (Map) loaderToCache.get(loader);
                if (cache == null) {
                    cache = new HashMap();
                    loaderToCache.put(loader, cache);
                }
            /*
             * This mapping will remain valid for the duration of this
             * method, without further synchronization, because the mapping
             * will only be removed if the class loader becomes unreachable.
             */
            }
    
        /*
         * Look up the list of interfaces in the proxy class cache using
         * the key.  This lookup will result in one of three possible
         * kinds of values:
         *     null, if there is currently no proxy class for the list of
         *         interfaces in the class loader,
         *     the pendingGenerationMarker object, if a proxy class for the
         *         list of interfaces is currently being generated,
         *     or a weak reference to a Class object, if a proxy class for
         *         the list of interfaces has already been generated.
         */
            synchronized (cache) {
            /*
             * Note that we need not worry about reaping the cache for
             * entries with cleared weak references because if a proxy class
             * has been garbage collected, its class loader will have been
             * garbage collected as well, so the entire cache will be reaped
             * from the loaderToCache map.
             */
                do {
                    Object value = cache.get(key);
                    if (value instanceof Reference) {
                        proxyClass = (Class) ((Reference) value).get();
                    }
                    if (proxyClass != null) {
                        // proxy class already generated: return it
                        return proxyClass;
                    } else if (value == pendingGenerationMarker) {
                        // proxy class being generated: wait for it
                        try {
                            cache.wait();
                        } catch (InterruptedException e) {
                /*
                 * The class generation that we are waiting for should
                 * take a small, bounded time, so we can safely ignore
                 * thread interrupts here.
                 */
                        }
                        continue;
                    } else {
                /*
                 * No proxy class for this list of interfaces has been
                 * generated or is being generated, so we will go and
                 * generate it now.  Mark it as pending generation.
                 */
                        cache.put(key, pendingGenerationMarker);
                        break;
                    }
                } while (true);
            }
            try {
                String proxyPkg = null;    // package to define proxy class in
            /*
             * Record the package of a non-public proxy interface so that the
             * proxy class will be defined in the same package.  Verify that
             * all non-public proxy interfaces are in the same package.
             */
            //寻找到package的包路径,为构建代理类做准备。同时要保证所有的非public代理接口在相同的包中
                for (int i = 0; i < interfaces.length; i++) {
                    int flags = interfaces[i].getModifiers();
                    if (!Modifier.isPublic(flags)) {
                        String name = interfaces[i].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");
                        }
                    }
                }
                if (proxyPkg == null) {    // if no non-public proxy interfaces,
                    proxyPkg = "";        // use the unnamed package
                }
                {
            /*
             * Choose a name for the proxy class to generate.
             */
                    long num;
                    synchronized (nextUniqueNumberLock) {
                        num = nextUniqueNumber++;
                    }
                    //这里构建了Class的全类名:包名+$Proxy+num,这里的proxyClassNamePrefix=“$Proxy”
                    String proxyName = proxyPkg + proxyClassNamePrefix + num;
            /*
             * Verify that the class loader hasn't already
             * defined a class with the chosen name.
             */
    
            /*
             * Generate the specified proxy class.
             */
                    //该方法为核心方法,获取代理类的字节码数据流。也即是proxyName.class文件的数据流
                    //因为interface的所有class文件都不已经被加载,所以这里只需要根据名称就可以从JVM中读取出所有的二进制数据
                    byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);
                    try {
                        //调用native方法从class字节码文件中创建代理类的Class实例。这里不再进入分析,由JVM负责实现
                        //获取到代理类的Class实例后,程序就可以根据代理类Class通过反射进行对象创建
                        proxyClass = 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());
                    }
                }
                // add to set of all generated proxy classes, for isProxyClass
                proxyClasses.put(proxyClass, null);
            } finally {
            /*
             * We must clean up the "pending generation" state of the proxy
             * class cache entry somehow.  If a proxy class was successfully
             * generated, store it in the cache (with a weak reference);
             * otherwise, remove the reserved entry.  In all cases, notify
             * all waiters on reserved entries in this cache.
             */
                synchronized (cache) {
                    if (proxyClass != null) {
                        cache.put(key, new WeakReference(proxyClass));
                    } else {
                        cache.remove(key);
                    }
                    cache.notifyAll();
                }
            }
            //返回代理类的Class实例
            return proxyClass;
        }
    }

     在上面的方法中,核心就在于如何构建代理类的class字节码文件。因为该字节码文件是由代理类和目标类组合而成,这也即是在运行期间动态创建class的方法。

     即:byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);该方法的具体实现,看下面代码分析

     public static byte[] generateProxyClass(final String proxyName, Class[] interfaces) {
            //创建代理生成器
            ProxyGenerator proxyGenerator = new ProxyGenerator(proxyName, interfaces);
            //调用generateClassFile()生成class的二进制数据流
            final byte[] classFile = proxyGenerator.generateClassFile();
            //该参数可以在运行时配置,当为true时则程序会生成代理类的class文件保存在磁盘中,即: $Proxy0.class
            if(saveGeneratedFiles) {
                AccessController.doPrivileged(new PrivilegedAction() {
                    public Object run() {
                        try {
                            FileOutputStream var1 = new FileOutputStream(ProxyGenerator.dotToSlash(proxyName) + ".class");
                            var1.write(classFile);
                            var1.close();
                            return null;
                        } catch (IOException var2) {
                            throw new InternalError("I/O exception saving generated file: " + interfaces);
                        }
                    }
                });
            }
            return classFile;
        }

     继续进入proxyGenerator.generateClassFile()分析

     private byte[] generateClassFile() {
            //注意这三个方法,hashCodeMethod、equalsMethod、toStringMethod
            //这三个方法来自于Object,代理类同样需要Object的这三个方法
            //把这三个方法名称和与之匹配的Object,class进行缓存
            this.addProxyMethod(hashCodeMethod, Object.class);
            this.addProxyMethod(equalsMethod, Object.class);
            this.addProxyMethod(toStringMethod, Object.class);
    
            int var1;
            int var3;
            //这里循环对目标类所实现的所有接口中的方法进行缓存
            for(var1 = 0; var1 < this.interfaces.length; ++var1) {
                //获取接口中的方法
                Method[] var2 = this.interfaces[var1].getMethods();
                for(var3 = 0; var3 < var2.length; ++var3) {
                    this.addProxyMethod(var2[var3], this.interfaces[var1]);
                }
            }
            Iterator var7 = this.proxyMethods.values().iterator();
            List var8;
            //把代理类需要创建的方法缓存在var8中。这里说明一下,因为该源码是从class中读取出来的,所以变量名在进行编译的时候被更改了,这里阅读的时候需要注意一些
            while(var7.hasNext()) {
                var8 = (List)var7.next();
                checkReturnTypes(var8);
            }
            Iterator var11;
            try {
                //添加构造器方法,至此代理类中所需要添加的方法添加完成
                this.methods.add(this.generateConstructor());
                var7 = this.proxyMethods.values().iterator();
                //循环把需要的变量和方法添加在fileds和methods中缓存
                while(var7.hasNext()) {
                    var8 = (List)var7.next();
                    var11 = var8.iterator();
                    while(var11.hasNext()) {
                        ProxyGenerator.ProxyMethod var4 = (ProxyGenerator.ProxyMethod)var11.next();
                        this.fields.add(new ProxyGenerator.FieldInfo(var4.methodFieldName, "Ljava/lang/reflect/Method;", 10));
                        this.methods.add(var4.generateMethod());
                    }
                }
                this.methods.add(this.generateStaticInitializer());
            } catch (IOException var6) {
                throw new InternalError("unexpected I/O Exception");
            }
            //对方法和参数进行安全性验证
            if(this.methods.size() > 'uffff') {
                throw new IllegalArgumentException("method limit exceeded");
            } else if(this.fields.size() > 'uffff') {
                throw new IllegalArgumentException("field limit exceeded");
            } else {
                this.cp.getClass(dotToSlash(this.className));
                this.cp.getClass("java/lang/reflect/Proxy");
                for(var1 = 0; var1 < this.interfaces.length; ++var1) {
                    this.cp.getClass(dotToSlash(this.interfaces[var1].getName()));
                }
                this.cp.setReadOnly();
                //构建缓冲流存放动态生成的字节码文件数据
                ByteArrayOutputStream var9 = new ByteArrayOutputStream();
                DataOutputStream var10 = new DataOutputStream(var9);
                try {
                    //这里就是按照class文件格式进行封装,这里不再详解
                    var10.writeInt(-889275714);
                    var10.writeShort(0);
                    var10.writeShort(49);
                    this.cp.write(var10);
                    var10.writeShort(49);
                    var10.writeShort(this.cp.getClass(dotToSlash(this.className)));
                    var10.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
                    var10.writeShort(this.interfaces.length);
                    for(var3 = 0; var3 < this.interfaces.length; ++var3) {
                        //添加所有方法的字节码数据
                        var10.writeShort(this.cp.getClass(dotToSlash(this.interfaces[var3].getName())));
                    }
                    var10.writeShort(this.fields.size());
                    var11 = this.fields.iterator();
                    while(var11.hasNext()) {
                        //添加所有变量的字节码数据
                        ProxyGenerator.FieldInfo var12 = (ProxyGenerator.FieldInfo)var11.next();
                        var12.write(var10);
                    }
                    var10.writeShort(this.methods.size());
                    var11 = this.methods.iterator();
                    while(var11.hasNext()) {
                        ProxyGenerator.MethodInfo var13 = (ProxyGenerator.MethodInfo)var11.next();
                        var13.write(var10);
                    }
                    var10.writeShort(0);
                    //动态组合class二进制字节码结束,进行返回。这里存储的就是一个完整class文件数据。调用处据此生成Class对象实例
                    return var9.toByteArray();
                } catch (IOException var5) {
                    throw new InternalError("unexpected I/O Exception");
                }
            }
        }

     至此,JDK动态代理的实现结束。由以上可见,其核心就是动态的生成代理类的class字节码数据,然后调用native方法从字节码数据中创建代理对象的Class实例,拿到Class实例后通过Java的反射技术生成代理类

     那么该代理类的结果到底如何?我们可以在main方法中添加 System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");配置,这样在程序运行的时候会把代理类的字节码文件保存在类路径下。通过反编译可以读取到代理类的具体详情。

    下面看反编译后的代理类文件

    package com.sun.proxy;
    
    import com.zpj.proxy.jdk.Person;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.UndeclaredThrowableException;
    
    //注意这里$Proxy0 extends Proxy implements Person,这里也即是目标类必须实现的有接口的原因
    /**
     * 代理类名称:$Proxy0
     * 继承:Proxy
     * 实现:Person
     */
    public final class $Proxy0 extends Proxy implements Person {
        private static Method m1;
        private static Method m2;
        private static Method m3;
        private static Method m0;
    
        public $Proxy0(InvocationHandler paramInvocationHandler) throws {
            super(paramInvocationHandler);
        }
    
        //继承自Object的方法
        public final boolean equals(Object paramObject) throws {
            try {
                return ((Boolean) this.h.invoke(this, m1, new Object[]{paramObject})).booleanValue();
            } catch (Error | RuntimeException localError) {
                throw localError;
            } catch (Throwable localThrowable) {
                throw new UndeclaredThrowableException(localThrowable);
            }
        }
        //继承自Object的方法
        public final String toString()throws {
            try {
                return (String) this.h.invoke(this, m2, null);
            } catch (Error | RuntimeException localError) {
                throw localError;
            } catch (Throwable localThrowable) {
                throw new UndeclaredThrowableException(localThrowable);
            }
        }
        //继承自Object的方法
        public final int hashCode()throws {
            try {
                return ((Integer) this.h.invoke(this, m0, null)).intValue();
            } catch (Error | RuntimeException localError) {
                throw localError;
            } catch (Throwable localThrowable) {
                throw new UndeclaredThrowableException(localThrowable);
            }
        }
        //实现Person的方法
        public final void doWork()throws {
            try {
                //由此处可以看出目标方法的调用循序
                /**
                 * 代理类先调用实现接口的方法,在该方法中调用InvocationHandler的invoke方法。
                 * 而在invoke中由通过注入进去的methods通过反射调用目标类的目标方法doWork()
                 */
                this.h.invoke(this, m3, null);
                return;
            } catch (Error | RuntimeException localError) {
                throw localError;
            } catch (Throwable localThrowable) {
                throw new UndeclaredThrowableException(localThrowable);
            }
        }
    
        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]);
                m3 = Class.forName("com.zpj.proxy.jdk.Person").getMethod("doWork", new Class[0]);
                m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
                return;
            } catch (NoSuchMethodException localNoSuchMethodException) {
                throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
            } catch (ClassNotFoundException localClassNotFoundException) {
                throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
            }
        }
    }

    ------------end

  • 相关阅读:
    【转】java面试题与答案
    【Appium】移动端自动化测试,触摸(TouchAction) 与多点触控(MultiAction)
    【转载】appium+python自动化-首次打开app权限弹窗问题
    【转载】视频直播平台测试
    关于http协议
    🍖MVC 与 MVT
    🍖Django框架之视图层(CBV源码剖析) ⭐
    🍖Django之settings源码分析
    🍖CGI、FastCGI、WSGI、uWSGI、uwsgi关系
    🐍Python内置函数
  • 原文地址:https://www.cnblogs.com/PerkinsZhu/p/6658066.html
Copyright © 2011-2022 走看看