美文网首页
JDK中的proxy动态代理原理剖析

JDK中的proxy动态代理原理剖析

作者: 编程小猪 | 来源:发表于2017-03-23 20:36 被阅读0次

    主要API类是:

    Proxy.newProxyInstance
    
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
                                   throws IllegalArgumentException
    返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。此方法相当于:
         Proxy.getProxyClass(loader, interfaces).
             getConstructor(new Class[] { InvocationHandler.class }).
             newInstance(new Object[] { handler });
    
    
    Proxy.newProxyInstance 抛出 IllegalArgumentException,原因与 Proxy.getProxyClass 相同。
    参数:
    loader - 定义代理类的类加载器
    interfaces - 代理类要实现的接口列表
    h - 指派方法调用的调用处理程序
    返回:
    一个带有代理类的指定调用处理程序的代理实例,它由指定的类加载器定义,并实现指定的接口
    抛出:
    IllegalArgumentException - 如果违反传递到 getProxyClass 的参数上的任何限制
    NullPointerException - 如果 interfaces 数组参数或其任何元素为 null,或如果调用处理程序 h 为 null
    

    先声明一个接口

    package com.czq.proxy;
    public interface IPackageManager {
         String getPackageInfo() ;
    }
    

    实现该接口

    package com.czq.proxy;
    public class PackageManagerImpl implements IPackageManager {
        @Override
        public String getPackageInfo() {
            String s = "com.czq.proxy";
    //        System.out.println(s);
            return s;
        }
        @Override
        public String toString() {
            return "PackageManagerImpl";
        }
    }
    

    实现InvocationHandler 接口,关键之处在这

    package com.czq.proxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    public class PackageManagerWoker implements InvocationHandler {
        private Object mTarget = null;
        public PackageManagerWoker(Object target) {
            super();
            this.mTarget = target;
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System. out.println("1" );
            System. out.println("method:" +method);
            if (args != null) {
                for (int i = 0; i < args.length; i++) {
                    System. out.println("args[" + i + "]:" + args[i]);
                }
            }
            Object result = method.invoke( mTarget, args);
            System. out.println("2" );
            return result;       
        }
    

    测试:

    package com.czq.proxy;
    
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    import java.lang.reflect.Modifier;
    import java.lang.reflect.Proxy;
    
    public class Test {
    
        public static void main(String[] args) {
            // 从源码中得知,设置这个值,可以把生成的代理类,输出出来。
            System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
    
           
            IPackageManager pkgManger = new PackageManagerImpl();
    //        System.out.println("pkgManger.toString:"+pkgManger.toString());
            PackageManagerWoker woker = new PackageManagerWoker(pkgManger);
            IPackageManager pm = (IPackageManager) Proxy.newProxyInstance(pkgManger.getClass().getClassLoader(), pkgManger
                    .getClass().getInterfaces(), woker);
           //  System.out.println("pm.getName:" +pm.getClass().getName())
        
            System. out.println("pm.toString:" +pm.toString());
            System.out.println(pm.getPackageInfo());
        } 
    }
    

    输出结果如下:

    1
    method:public java.lang.String java.lang.Object.toString()
    2
    pm.toString:PackageManagerImpl
    1
    method:public abstract java.lang.String com.czq.proxy.IPackageManager.getPackageInfo()
    2
    com.czq.proxy
    

    得出结论:
    pm.getPackageInfo()方法会走到PackageManagerWoker的invoke方法。
    思考问题:
    PackageManagerWoker不继承IPackageManager。不能强转成IPackageManager。
    也就是pm对象不是PackageManagerWoker对象。
    那pm 是哪个对象,是什么类呢?为什么还能强转成IPackageManager

    打印pm的className

    System.out.println("pm.getName:" +pm.getClass().getName())
    

    得出的结果:

    pm.getName:com.sun.proxy.$Proxy0
    

    也就是pm对象是com.sun.proxy.$Proxy0这个类new出的对象。这个类是刚刚Proxy.newProxyInstance自动生成的class
    那这个class里面写的是什么呢?
    查看源码:Proxy.java

    /**
         * 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) {
    
                Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length );
                for (Class<?> intf : interfaces) {
                    /*
                     * Verify that the class loader resolves the name of this
                     * interface to the same Class object.
                     */
                    Class<?> interfaceClass = null;
                    try {
                        interfaceClass = Class.forName(intf.getName(), false, loader);
                    } catch (ClassNotFoundException e) {
                    }
                    if (interfaceClass != intf) {
                        throw new IllegalArgumentException(
                            intf + " 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.put(interfaceClass, Boolean.TRUE) != null ) {
                        throw new IllegalArgumentException(
                            "repeated interface: " + interfaceClass.getName());
                    }
                }
    
                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.
                 */
                for (Class<?> intf : interfaces) {
                    int flags = intf.getModifiers();
                    if (!Modifier.isPublic(flags)) {
                        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");
                        }
                    }
                }
    
                if (proxyPkg == null) {
                    // if no non-public proxy interfaces, use com.sun.proxy package
                    proxyPkg = ReflectUtil. PROXY_PACKAGE + ".";
                }
    
                /*
                 * 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);
                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());
                }
            }
        }
    

    通过ProxyGenerator 生成了这个class。
    查看ProxyGenerator源码:

    /**
         * Generate a proxy class given a name and a list of proxy interfaces.
         *
         * @param name        the class name of the proxy class
         * @param interfaces  proxy interfaces
         * @param accessFlags access flags of the proxy class
        */
        public static byte[] generateProxyClass(final String name,
                                                Class<?>[] interfaces,
                                                int accessFlags)
        {
            ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags);
            final byte[] classFile = gen.generateClassFile();
    
            if (saveGeneratedFiles) {
                java.security.AccessController.doPrivileged(
                new java.security.PrivilegedAction<Void>() {
                    public Void run() {
                        try {
                            int i = name.lastIndexOf('.');
                            Path path;
                            if (i > 0) {
                                Path dir = Paths.get(name.substring(0, i).replace('.', File.separatorChar));
                                Files.createDirectories(dir);
                                path = dir.resolve(name.substring(i+1, name.length()) + ".class");
                            } else {
                                path = Paths.get(name + ".class");
                            }
                            Files.write(path, classFile);
                            return null;
                        } catch (IOException e) {
                            throw new InternalError(
                                "I/O exception saving generated file: " + e);
                        }
                    }
                });
            }
    
            return classFile;
        }
    

    发现 saveGeneratedFiles 为true报错生成的class的源码。
    这个saveGeneratedFiles 怎么赋值呢?

     /** debugging flag for saving generated class files */
        private final static boolean saveGeneratedFiles =
            java.security.AccessController.doPrivileged(
                new GetBooleanAction(
                    "sun.misc.ProxyGenerator.saveGeneratedFiles")).booleanValue();
    

    也就是把sun.misc.ProxyGenerator.saveGeneratedFiles 改成true就可以输出结果了。

            // 从源码中得知,设置这个值,可以把生成的代理类,输出出来。      
    
    System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
    

    注意,需要再工程根目录下,增加 com/sun/proxy目录,否则会报错如下:

    Exception in thread "main" java.lang.InternalError: I/O exception saving generated file: java.io.FileNotFoundException : com\sun\proxy\$Proxy0.class (系统找不到指定的路径。)
         at sun.misc.ProxyGenerator$1.run(ProxyGenerator.java:336 )
         at sun.misc.ProxyGenerator$1.run(ProxyGenerator.java:327 )
         at java.security.AccessController.doPrivileged(Native Method)
         at sun.misc.ProxyGenerator.generateProxyClass(ProxyGenerator.java:326)
         at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:672)
         at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:592)
         at java.lang.reflect.WeakCache$Factory.get(WeakCache.java:244)
         at java.lang.reflect.WeakCache.get(WeakCache.java:141 )
         at java.lang.reflect.Proxy.getProxyClass0(Proxy.java:455 )
         at java.lang.reflect.Proxy.newProxyInstance(Proxy.java:738)
         at com.czq.proxy.Test.main( Test.java:18)
    

    把proxy0输出的结果如下:
    反编译看看proxy0是内容是啥,有什么秘密

    package com.sun.proxy;
    
    import com.czq.proxy.IPackageManager;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.UndeclaredThrowableException;
    
    public final class $Proxy0 extends Proxy
      implements IPackageManager
    {
      private static Method m3; // 生成对应的方法对象
      private static Method m1;
      private static Method m0;
      private static Method m2;
    // proxy0 继承Proxy,实现IPackageManager 接口,需要传入 InvocationHandler,初始化对应的h对象。
    // 我们的h对象就是PackageManagerWoker,所以我们会调用到PackageManagerWoker的 invoke方法。
    // 所以是proxy0,调用InvocationHandler的 invoke 方法,传入对应的方法。InvocationHandler 放射调用对应的tagret中的方法。
      public $Proxy0(InvocationHandler paramInvocationHandler)
        throws
      {
        super(paramInvocationHandler);
      }
    
      public final String getPackageInfo()
        throws
      {
        try
        {
          return (String)this.h.invoke(this, m3, null);
        }
        catch (RuntimeException localRuntimeException)
        {
          throw localRuntimeException;
        }
        catch (Throwable localThrowable)
        {
        }
        throw new UndeclaredThrowableException(localThrowable);
      }
    
      public final boolean equals(Object paramObject)
        throws
      {
        try
        {
          return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
        }
        catch (RuntimeException localRuntimeException)
        {
          throw localRuntimeException;
        }
        catch (Throwable localThrowable)
        {
        }
        throw new UndeclaredThrowableException(localThrowable);
      }
    
      public final int hashCode()
        throws
      {
        try
        {
          return ((Integer)this.h.invoke(this, m0, null)).intValue();
        }
        catch (RuntimeException localRuntimeException)
        {
          throw localRuntimeException;
        }
        catch (Throwable localThrowable)
        {
        }
        throw new UndeclaredThrowableException(localThrowable);
      }
    
      public final String toString()
        throws
      {
        try
        {
          return (String)this.h.invoke(this, m2, null);
        }
        catch (RuntimeException localRuntimeException)
        {
          throw localRuntimeException;
        }
        catch (Throwable localThrowable)
        {
        }
        throw new UndeclaredThrowableException(localThrowable);
      }
    
      static
      {
        try
        {
         // 把各个方法,对应到成员变量上
          m3 = Class.forName("com.czq.proxy.IPackageManager").getMethod("getPackageInfo", new Class[0]);
          m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
          m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
          m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
          return;
        }
        catch (NoSuchMethodException localNoSuchMethodException)
        {
          throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
        }
        catch (ClassNotFoundException localClassNotFoundException)
        {
        }
        throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
      }
    }
    

    结论如下:

    1. proxy0 继承Proxy,实现IPackageManager 接口,需要传入 InvocationHandler,初始化对应的h对象。
    2. 我们的h对象就是PackageManagerWoker,所以我们会调用到PackageManagerWoker的 invoke方法。
    3. 所以是proxy0,调用InvocationHandler的 invoke 方法,传入对应的方法。InvocationHandler 放射调用对应的tagret中的方法。套了2层
      整体类图如下
    类图.png

    相关文章

      网友评论

          本文标题:JDK中的proxy动态代理原理剖析

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