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

来源:互联网 发布:嗟乎,此真将军矣 编辑:程序博客网 时间:2024/05/23 05:06

主要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());    } }

输出结果如下:
1method:public java.lang.String java.lang.Object.toString()2pm.toString:PackageManagerImpl1method:public abstract java.lang.String com.czq.proxy.IPackageManager.getPackageInfo()2com.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层



0 0
原创粉丝点击