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

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

作者: dffd001V | 来源:发表于2019-03-25 21:24 被阅读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](https://img.haomeiwen.com/i5251070/4167c3297812f603.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

    相关文章

      网友评论

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

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