细说JDK动态代理的实现原理

原文:http://blog.csdn.net/mhmyqn/article/details/48474815

关于JDK的动态代理,最为人熟知的可能要数spring AOP的实现,默认情况下,Spring AOP的实现对于接口来说就是使用的JDK的动态代理来实现的,而对于类的代理使用CGLIB来实现。那么,什么是JDK的动态代理呢?

JDK的动态代理,就是在程序运行的过程中,根据被代理的接口来动态生成代理类的class文件,并加载运行的过程。JDK从1.3开始支持动态代理。那么JDK是如何生成动态代理的呢?JDK动态代理为什么不支持类的代理,只支持接口的代理?

首先来看一下如何使用JDK动态代理。JDK提供了Java.lang.reflect.Proxy类来实现动态代理的,可通过它的newProxyInstance来获得代理实现类。同时对于代理的接口的实际处理,是一个java.lang.reflect.InvocationHandler,它提供了一个invoke方法供实现者提供相应的代理逻辑的实现。可以对实际的实现进行一些特殊的处理,像Spring AOP中的各种advice。下面来看看如何使用。

被代理的接口

  1. package com.mikan.proxy;  
  2.   
  3. /** 
  4.  * @author Mikan 
  5.  * @date 2015-09-15 18:00 
  6.  */  
  7. public interface HelloWorld {  
  8.   
  9.     void sayHello(String name);  
  10.   
  11. }  
package com.mikan.proxy;

/**
 * @author Mikan
 * @date 2015-09-15 18:00
 */
public interface HelloWorld {

    void sayHello(String name);

}
接口的实现类:

  1. package com.mikan.proxy;  
  2.   
  3. /** 
  4.  * @author Mikan 
  5.  * @date 2015-09-15 18:01 
  6.  */  
  7. public class HelloWorldImpl implements HelloWorld {  
  8.     @Override  
  9.     public void sayHello(String name) {  
  10.         System.out.println(”Hello ” + name);  
  11.     }  
  12. }  
package com.mikan.proxy;

/**
 * @author Mikan
 * @date 2015-09-15 18:01
 */
public class HelloWorldImpl implements HelloWorld {
    @Override
    public void sayHello(String name) {
        System.out.println("Hello " + name);
    }
}
实现一个java.lang.reflect.InvocationHandler:

  1. package com.mikan.proxy;  
  2.   
  3. import java.lang.reflect.InvocationHandler;  
  4. import java.lang.reflect.Method;  
  5.   
  6. /** 
  7.  * @author Mikan 
  8.  * @date 2015-09-15 19:53 
  9.  */  
  10. public class CustomInvocationHandler implements InvocationHandler {  
  11.     private Object target;  
  12.   
  13.     public CustomInvocationHandler(Object target) {  
  14.         this.target = target;  
  15.     }  
  16.   
  17.     @Override  
  18.     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
  19.         System.out.println(”Before invocation”);  
  20.         Object retVal = method.invoke(target, args);  
  21.         System.out.println(”After invocation”);  
  22.         return retVal;  
  23.     }  
  24. }  
package com.mikan.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * @author Mikan
 * @date 2015-09-15 19:53
 */
public class CustomInvocationHandler implements InvocationHandler {
    private Object target;

    public CustomInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before invocation");
        Object retVal = method.invoke(target, args);
        System.out.println("After invocation");
        return retVal;
    }
}
使用代理:

  1. package com.mikan.proxy;  
  2.   
  3. import java.lang.reflect.Proxy;  
  4.   
  5. /** 
  6.  * @author Mikan 
  7.  * @date 2015-09-15 18:01 
  8.  */  
  9. public class ProxyTest {  
  10.   
  11.     public static void main(String[] args) throws Exception {  
  12.         System.getProperties().put(”sun.misc.ProxyGenerator.saveGeneratedFiles”“true”);  
  13.   
  14.         CustomInvocationHandler handler = new CustomInvocationHandler(new HelloWorldImpl());  
  15.         HelloWorld proxy = (HelloWorld) Proxy.newProxyInstance(  
  16.                 ProxyTest.class.getClassLoader(),  
  17.                 new Class[]{HelloWorld.class},  
  18.                 handler);  
  19.         proxy.sayHello(”Mikan”);  
  20.     }  
  21.   
  22. }  
package com.mikan.proxy;

import java.lang.reflect.Proxy;

/**
 * @author Mikan
 * @date 2015-09-15 18:01
 */
public class ProxyTest {

    public static void main(String[] args) throws Exception {
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

        CustomInvocationHandler handler = new CustomInvocationHandler(new HelloWorldImpl());
        HelloWorld proxy = (HelloWorld) Proxy.newProxyInstance(
                ProxyTest.class.getClassLoader(),
                new Class[]{HelloWorld.class},
                handler);
        proxy.sayHello("Mikan");
    }

}
运行的输出结果:

  1. localhost:classes mikan&nbsp;java&nbsp;com/mikan/proxy/ProxyTest&nbsp;&nbsp;</span></span></li><li class=""><span>Before&nbsp;invocation&nbsp;&nbsp;</span></li><li class="alt"><span>Hello&nbsp;Mikan&nbsp;&nbsp;</span></li><li class=""><span>After&nbsp;invocation&nbsp;&nbsp;</span></li></ol><div class="save_code tracking-ad" data-mod="popu_249"><a href="javascript:;" target="_blank"><img src="http://static.blog.csdn.net/images/save_snippets.png"></a></div></div><pre code_snippet_id="2307488" snippet_file_name="blog_20170402_5_5323369" name="code" class="plain" style="display: none;">localhost:classes mikan java com/mikan/proxy/ProxyTest Before invocation Hello Mikan After invocation从上面可以看出,JDK的动态代理使用起来非常简单,但是只知道如何使用是不够的,知其然,还需知其所以然。所以要想搞清楚它的实现,那么得从源码入手。这里的源码是1.7.0_79。首先来看看它是如何生成代理类的:

    1. public static Object newProxyInstance(ClassLoader loader,  
    2.                                       Class<?>[] interfaces,  
    3.                                       InvocationHandler h)  
    4.     throws IllegalArgumentException {  
    5.     if (h == null) {  
    6.         throw new NullPointerException();  
    7.     }  
    8.   
    9.     final Class<?>[] intfs = interfaces.clone();  
    10.     final SecurityManager sm = System.getSecurityManager();  
    11.     if (sm != null) {  
    12.         checkProxyAccess(Reflection.getCallerClass(), loader, intfs);  
    13.     }  
    14.     // 这里是生成class的地方  
    15.     Class<?> cl = getProxyClass0(loader, intfs);  
    16.     // 使用我们实现的InvocationHandler作为参数调用构造方法来获得代理类的实例  
    17.     try {  
    18.         final Constructor<?> cons = cl.getConstructor(constructorParams);  
    19.         final InvocationHandler ih = h;  
    20.         if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {  
    21.             return AccessController.doPrivileged(new PrivilegedAction<Object>() {  
    22.                 public Object run() {  
    23.                     return newInstance(cons, ih);  
    24.                 }  
    25.             });  
    26.         } else {  
    27.             return newInstance(cons, ih);  
    28.         }  
    29.     } catch (NoSuchMethodException e) {  
    30.         throw new InternalError(e.toString());  
    31.     }  
    32. }  
        public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)
            throws IllegalArgumentException {
            if (h == null) {
                throw new NullPointerException();
            }
    
            final Class<?>[] intfs = interfaces.clone();
            final SecurityManager sm = System.getSecurityManager();
            if (sm != null) {
                checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
            }
            // 这里是生成class的地方
            Class<?> cl = getProxyClass0(loader, intfs);
            // 使用我们实现的InvocationHandler作为参数调用构造方法来获得代理类的实例
            try {
                final Constructor<?> cons = cl.getConstructor(constructorParams);
                final InvocationHandler ih = h;
                if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
                    return AccessController.doPrivileged(new PrivilegedAction<Object>() {
                        public Object run() {
                            return newInstance(cons, ih);
                        }
                    });
                } else {
                    return newInstance(cons, ih);
                }
            } catch (NoSuchMethodException e) {
                throw new InternalError(e.toString());
            }
        }
    其中newInstance只是调用Constructor.newInstance来构造相应的代理类实例,这里重点是看getProxyClass0这个方法的实现:

    1. private static Class<?> getProxyClass0(ClassLoader loader,  
    2.                                        Class<?>… interfaces) {  
    3.     // 代理的接口数量不能超过65535(没有这种变态吧)  
    4.     if (interfaces.length > 65535) {  
    5.         throw new IllegalArgumentException(“interface limit exceeded”);  
    6.     }  
    7.     // JDK对代理进行了缓存,如果已经存在相应的代理类,则直接返回,否则才会通过ProxyClassFactory来创建代理  
    8.     return proxyClassCache.get(loader, interfaces);  
    9. }  
        private static Class<?> getProxyClass0(ClassLoader loader,
                                               Class<?>... interfaces) {
            // 代理的接口数量不能超过65535(没有这种变态吧)
            if (interfaces.length > 65535) {
                throw new IllegalArgumentException("interface limit exceeded");
            }
            // JDK对代理进行了缓存,如果已经存在相应的代理类,则直接返回,否则才会通过ProxyClassFactory来创建代理
            return proxyClassCache.get(loader, interfaces);
        }
    其中代理缓存是使用WeakCache实现的,如下

    1. private static final WeakCache<ClassLoader, Class<?>[], Class<?>>  
    2.     proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());  
        private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
            proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
    具体的缓存逻辑这里暂不关心,只需要关心ProxyClassFactory是如何生成代理类的,ProxyClassFactory是Proxy的一个静态内部类,实现了WeakCache的内部接口BiFunction的apply方法:

    1. private static final class ProxyClassFactory  
    2.     implements BiFunction<ClassLoader, Class<?>[], Class<?>> {  
    3.     // 所有代理类名字的前缀  
    4.     private static final String proxyClassNamePrefix = Proxy"</span><span>;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;</span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;用于生成代理类名字的计数器</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">private</span><span>&nbsp;</span><span class="keyword">static</span><span>&nbsp;</span><span class="keyword">final</span><span>&nbsp;AtomicLong&nbsp;nextUniqueNumber&nbsp;=&nbsp;</span><span class="keyword">new</span><span>&nbsp;AtomicLong();&nbsp;&nbsp;</span></span></li><li class=""><span>&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="annotation">@Override</span><span>&nbsp;&nbsp;</span></span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">public</span><span>&nbsp;Class&lt;?&gt;&nbsp;apply(ClassLoader&nbsp;loader,&nbsp;Class&lt;?&gt;[]&nbsp;interfaces)&nbsp;{&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;省略验证代理接口的代码……</span><span>&nbsp;&nbsp;</span></span></li><li class=""><span>&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;proxyPkg&nbsp;=&nbsp;<span class="keyword">null</span><span>;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//&nbsp;生成的代理类的包名</span><span>&nbsp;&nbsp;</span></span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;对于非公共接口,代理类的包名与接口的相同</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">for</span><span>&nbsp;(Class&lt;?&gt;&nbsp;intf&nbsp;:&nbsp;interfaces)&nbsp;{&nbsp;&nbsp;</span></span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">int</span><span>&nbsp;flags&nbsp;=&nbsp;intf.getModifiers();&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;(!Modifier.isPublic(flags))&nbsp;{&nbsp;&nbsp;</span></span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;name&nbsp;=&nbsp;intf.getName();&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">int</span><span>&nbsp;n&nbsp;=&nbsp;name.lastIndexOf(</span><span class="string">'.'</span><span>);&nbsp;&nbsp;</span></span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;pkg&nbsp;=&nbsp;((n&nbsp;==&nbsp;-<span class="number">1</span><span>)&nbsp;?&nbsp;</span><span class="string">""</span><span>&nbsp;:&nbsp;name.substring(</span><span class="number">0</span><span>,&nbsp;n&nbsp;+&nbsp;</span><span class="number">1</span><span>));&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;(proxyPkg&nbsp;==&nbsp;</span><span class="keyword">null</span><span>)&nbsp;{&nbsp;&nbsp;</span></span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;proxyPkg&nbsp;=&nbsp;pkg;&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span class="keyword">else</span><span>&nbsp;</span><span class="keyword">if</span><span>&nbsp;(!pkg.equals(proxyPkg))&nbsp;{&nbsp;&nbsp;</span></span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">throw</span><span>&nbsp;</span><span class="keyword">new</span><span>&nbsp;IllegalArgumentException(&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="string">"non-public&nbsp;interfaces&nbsp;from&nbsp;different&nbsp;packages"</span><span>);&nbsp;&nbsp;</span></span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;</span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;对于公共接口的包名,默认为com.sun.proxy</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;(proxyPkg&nbsp;==&nbsp;</span><span class="keyword">null</span><span>)&nbsp;{&nbsp;&nbsp;</span></span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;proxyPkg&nbsp;=&nbsp;ReflectUtil.PROXY_PACKAGE&nbsp;+&nbsp;<span class="string">"."</span><span>;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li><li class=""><span>&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;获取计数</span><span>&nbsp;&nbsp;</span></span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">long</span><span>&nbsp;num&nbsp;=&nbsp;nextUniqueNumber.getAndIncrement();&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;默认情况下,代理类的完全限定名为:com.sun.proxy.Proxy0,com.sun.proxy.$Proxy1……依次递增  
    5.         String proxyName = proxyPkg + proxyClassNamePrefix + num;  
    6.   
    7.         // 这里才是真正的生成代理类的字节码的地方  
    8.         byte[] proxyClassFile = ProxyGenerator.generateProxyClass(  
    9.             proxyName, interfaces);  
    10.         try {  
    11.             // 根据二进制字节码返回相应的Class实例  
    12.             return defineClass0(loader, proxyName,  
    13.                                 proxyClassFile, 0, proxyClassFile.length);  
    14.         } catch (ClassFormatError e) {  
    15.             throw new IllegalArgumentException(e.toString());  
    16.         }  
    17.     }  
    18. }  
        private static final class ProxyClassFactory
            implements BiFunction<ClassLoader, Class<?>[], Class<?>> {
            // 所有代理类名字的前缀
            private static final String proxyClassNamePrefix = "$Proxy";
    
            // 用于生成代理类名字的计数器
            private static final AtomicLong nextUniqueNumber = new AtomicLong();
    
            @Override
            public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
                // 省略验证代理接口的代码……
    
                String proxyPkg = null;     // 生成的代理类的包名
                // 对于非公共接口,代理类的包名与接口的相同
                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");
                        }
                    }
                }
    
                // 对于公共接口的包名,默认为com.sun.proxy
                if (proxyPkg == null) {
                    proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
                }
    
                // 获取计数
                long num = nextUniqueNumber.getAndIncrement();
                // 默认情况下,代理类的完全限定名为:com.sun.proxy.$Proxy0,com.sun.proxy.$Proxy1……依次递增
                String proxyName = proxyPkg + proxyClassNamePrefix + num;
    
                // 这里才是真正的生成代理类的字节码的地方
                byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                    proxyName, interfaces);
                try {
                    // 根据二进制字节码返回相应的Class实例
                    return defineClass0(loader, proxyName,
                                        proxyClassFile, 0, proxyClassFile.length);
                } catch (ClassFormatError e) {
                    throw new IllegalArgumentException(e.toString());
                }
            }
        }
    ProxyGenerator是sun.misc包中的类,它没有开源,但是可以反编译来一探究竟:

    1. public static byte[] generateProxyClass(final String var0, Class[] var1) {  
    2.     ProxyGenerator var2 = new ProxyGenerator(var0, var1);  
    3.     final byte[] var3 = var2.generateClassFile();  
    4.     // 这里根据参数配置,决定是否把生成的字节码(.class文件)保存到本地磁盘,我们可以通过把相应的class文件保存到本地,再反编译来看看具体的实现,这样更直观  
    5.     if(saveGeneratedFiles) {  
    6.         AccessController.doPrivileged(new PrivilegedAction() {  
    7.             public Void run() {  
    8.                 try {  
    9.                     FileOutputStream var1 = new FileOutputStream(ProxyGenerator.dotToSlash(var0) + “.class”);  
    10.                     var1.write(var3);  
    11.                     var1.close();  
    12.                     return null;  
    13.                 } catch (IOException var2) {  
    14.                     throw new InternalError(“I/O exception saving generated file: ” + var2);  
    15.                 }  
    16.             }  
    17.         });  
    18.     }  
    19.     return var3;  
    20. }  
        public static byte[] generateProxyClass(final String var0, Class[] var1) {
            ProxyGenerator var2 = new ProxyGenerator(var0, var1);
            final byte[] var3 = var2.generateClassFile();
            // 这里根据参数配置,决定是否把生成的字节码(.class文件)保存到本地磁盘,我们可以通过把相应的class文件保存到本地,再反编译来看看具体的实现,这样更直观
            if(saveGeneratedFiles) {
                AccessController.doPrivileged(new PrivilegedAction() {
                    public Void run() {
                        try {
                            FileOutputStream var1 = new FileOutputStream(ProxyGenerator.dotToSlash(var0) + ".class");
                            var1.write(var3);
                            var1.close();
                            return null;
                        } catch (IOException var2) {
                            throw new InternalError("I/O exception saving generated file: " + var2);
                        }
                    }
                });
            }
            return var3;
        }
    saveGeneratedFiles这个属性的值从哪里来呢:

    1. private static final boolean saveGeneratedFiles = ((Boolean)AccessController.doPrivileged(new GetBooleanAction(“sun.misc.ProxyGenerator.saveGeneratedFiles”))).booleanValue();  
        private static final boolean saveGeneratedFiles = ((Boolean)AccessController.doPrivileged(new GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"))).booleanValue();
    GetBooleanAction实际上是调用Boolean.getBoolean(propName)来获得的,而Boolean.getBoolean(propName)调用了System.getProperty(name),所以我们可以设置sun.misc.ProxyGenerator.saveGeneratedFiles这个系统属性为true来把生成的class保存到本地文件来查看。

    这里要注意,当把这个属性设置为true时,生成的class文件及其所在的路径都需要提前创建,否则会抛出FileNotFoundException异常。如:

    1. Exception in thread “main” java.lang.InternalError: I/O exception saving generated file: java.io.FileNotFoundException: com/sun/proxy/Proxy0.</span><span class="keyword">class</span><span>&nbsp;(No&nbsp;such&nbsp;file&nbsp;or&nbsp;directory)&nbsp;&nbsp;</span></span></li><li class=""><span>at&nbsp;sun.misc.ProxyGenerator1.run(ProxyGenerator.java:336)  
    2. at sun.misc.ProxyGenerator<span class="number">1</span><span>.run(ProxyGenerator.java:</span><span class="number">327</span><span>)&nbsp;&nbsp;</span></span></li><li class=""><span>at&nbsp;java.security.AccessController.doPrivileged(Native&nbsp;Method)&nbsp;&nbsp;</span></li><li class="alt"><span>at&nbsp;sun.misc.ProxyGenerator.generateProxyClass(ProxyGenerator.java:<span class="number">326</span><span>)&nbsp;&nbsp;</span></span></li><li class=""><span>at&nbsp;java.lang.reflect.Proxy<script type="math/tex" id="MathJax-Element-112">1.run(ProxyGenerator.java:327)  
    3. at java.security.AccessController.doPrivileged(Native Method)  
    4. at sun.misc.ProxyGenerator.generateProxyClass(ProxyGenerator.java:326)  
    5. at java.lang.reflect.Proxy</script>ProxyClassFactory.apply(Proxy.java:672)  
    6. at java.lang.reflect.ProxyProxyClassFactory.apply(Proxy.java:<span class="number">592</span><span>)&nbsp;&nbsp;</span></span></li><li class=""><span>at&nbsp;java.lang.reflect.WeakCacheFactory.get(WeakCache.java:244)  
    7. at java.lang.reflect.WeakCache.get(WeakCache.java:141)  
    8. at java.lang.reflect.Proxy.getProxyClass0(Proxy.java:455)  
    9. at java.lang.reflect.Proxy.newProxyInstance(Proxy.java:738)  
    10. at com.mikan.proxy.ProxyTest.main(ProxyTest.java:15)  
    11. at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)  
    12. at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)  
    13. at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)  
    14. at java.lang.reflect.Method.invoke(Method.java:606)  
    15. at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)  
    Exception in thread "main" java.lang.InternalError: I/O exception saving generated file: java.io.FileNotFoundException: com/sun/proxy/$Proxy0.class (No such file or directory)
    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.mikan.proxy.ProxyTest.main(ProxyTest.java:15)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
    即我们要在运行当前main方法的路径下创建com/sun/proxy目录,并创建一个$Proxy0.class文件,才能够正常运行并保存class文件内容。

    反编译$Proxy0.class文件,如下所示:

    1. package com.sun.proxy;  
    2.   
    3. import com.mikan.proxy.HelloWorld;  
    4. import java.lang.reflect.InvocationHandler;  
    5. import java.lang.reflect.Method;  
    6. import java.lang.reflect.Proxy;  
    7. import java.lang.reflect.UndeclaredThrowableException;  
    8.   
    9. public final class Proxy0&nbsp;</span><span class="keyword">extends</span><span>&nbsp;Proxy&nbsp;</span><span class="keyword">implements</span><span>&nbsp;HelloWorld&nbsp;{&nbsp;&nbsp;</span></span></li><li class=""><span>&nbsp;&nbsp;<span class="keyword">private</span><span>&nbsp;</span><span class="keyword">static</span><span>&nbsp;Method&nbsp;m1;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;<span class="keyword">private</span><span>&nbsp;</span><span class="keyword">static</span><span>&nbsp;Method&nbsp;m3;&nbsp;&nbsp;</span></span></li><li class=""><span>&nbsp;&nbsp;<span class="keyword">private</span><span>&nbsp;</span><span class="keyword">static</span><span>&nbsp;Method&nbsp;m0;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;<span class="keyword">private</span><span>&nbsp;</span><span class="keyword">static</span><span>&nbsp;Method&nbsp;m2;&nbsp;&nbsp;</span></span></li><li class=""><span>&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;<span class="keyword">public</span><span>&nbsp;Proxy0(InvocationHandler paramInvocationHandler) {  
    10.     super(paramInvocationHandler);  
    11.   }  
    12.   
    13.   public final boolean equals(Object paramObject) {  
    14.     try {  
    15.       return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();  
    16.     }  
    17.     catch (Error|RuntimeException localError) {  
    18.       throw localError;  
    19.     }  
    20.     catch (Throwable localThrowable) {  
    21.       throw new UndeclaredThrowableException(localThrowable);  
    22.     }  
    23.   }  
    24.   
    25.   public final void sayHello(String paramString) {  
    26.     try {  
    27.       this.h.invoke(this, m3, new Object[] { paramString });  
    28.       return;  
    29.     }  
    30.     catch (Error|RuntimeException localError) {  
    31.       throw localError;  
    32.     }  
    33.     catch (Throwable localThrowable) {  
    34.       throw new UndeclaredThrowableException(localThrowable);  
    35.     }  
    36.   }  
    37.   
    38.   public final int hashCode() {  
    39.     try {  
    40.       return ((Integer)this.h.invoke(this, m0, null)).intValue();  
    41.     }  
    42.     catch (Error|RuntimeException localError) {  
    43.       throw localError;  
    44.     }  
    45.     catch (Throwable localThrowable) {  
    46.       throw new UndeclaredThrowableException(localThrowable);  
    47.     }  
    48.   }  
    49.   
    50.   public final String toString() {  
    51.     try {  
    52.       return (String)this.h.invoke(this, m2, null);  
    53.     }  
    54.     catch (Error|RuntimeException localError) {  
    55.       throw localError;  
    56.     }  
    57.     catch (Throwable localThrowable) {  
    58.       throw new UndeclaredThrowableException(localThrowable);  
    59.     }  
    60.   }  
    61.   
    62.   static {  
    63.     try {  
    64.       m1 = Class.forName(”java.lang.Object”).getMethod(“equals”new Class[] { Class.forName(“java.lang.Object”) });  
    65.       m3 = Class.forName(”com.mikan.proxy.HelloWorld”).getMethod(“sayHello”new Class[] { Class.forName(“java.lang.String”) });  
    66.       m0 = Class.forName(”java.lang.Object”).getMethod(“hashCode”new Class[0]);  
    67.       m2 = Class.forName(”java.lang.Object”).getMethod(“toString”new Class[0]);  
    68.       return;  
    69.     }  
    70.     catch (NoSuchMethodException localNoSuchMethodException) {  
    71.       throw new NoSuchMethodError(localNoSuchMethodException.getMessage());  
    72.     }  
    73.     catch (ClassNotFoundException localClassNotFoundException) {  
    74.       throw new NoClassDefFoundError(localClassNotFoundException.getMessage());  
    75.     }  
    76.   }  
    77. }  
    package com.sun.proxy;
    
    import com.mikan.proxy.HelloWorld;
    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 HelloWorld {
      private static Method m1;
      private static Method m3;
      private static Method m0;
      private static Method m2;
    
      public $Proxy0(InvocationHandler paramInvocationHandler) {
        super(paramInvocationHandler);
      }
    
      public final boolean equals(Object paramObject) {
        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);
        }
      }
    
      public final void sayHello(String paramString) {
        try {
          this.h.invoke(this, m3, new Object[] { paramString });
          return;
        }
        catch (Error|RuntimeException localError) {
          throw localError;
        }
        catch (Throwable localThrowable) {
          throw new UndeclaredThrowableException(localThrowable);
        }
      }
    
      public final int hashCode() {
        try {
          return ((Integer)this.h.invoke(this, m0, null)).intValue();
        }
        catch (Error|RuntimeException localError) {
          throw localError;
        }
        catch (Throwable localThrowable) {
          throw new UndeclaredThrowableException(localThrowable);
        }
      }
    
      public final String toString() {
        try {
          return (String)this.h.invoke(this, m2, null);
        }
        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") });
          m3 = Class.forName("com.mikan.proxy.HelloWorld").getMethod("sayHello", new Class[] { Class.forName("java.lang.String") });
          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. 继承了Proxy类,实现了代理的接口,由于java不能多继承,这里已经继承了Proxy类了,不能再继承其他的类,所以JDK的动态代理不支持对实现类的代理,只支持接口的代理。
    2. 提供了一个使用InvocationHandler作为参数的构造方法。
    3. 生成静态代码块来初始化接口中方法的Method对象,以及Object类的equals、hashCode、toString方法。
    4. 重写了Object类的equals、hashCode、toString,它们都只是简单的调用了InvocationHandler的invoke方法,即可以对其进行特殊的操作,也就是说JDK的动态代理还可以代理上述三个方法。
    5. 代理类实现代理接口的sayHello方法中,只是简单的调用了InvocationHandler的invoke方法,我们可以在invoke方法中进行一些特殊操作,甚至不调用实现的方法,直接返回。

    至此JDK动态代理的实现原理就分析的差不多了。同时我们可以想像一下Spring AOP提供的各种拦截该如何实现,就已经很明了了,如下所示:

    1. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
    2.     // BeforeAdvice  
    3.     Object retVal = null;  
    4.     try {  
    5.         // AroundAdvice  
    6.         retVal = method.invoke(target, args);  
    7.         // AroundAdvice  
    8.         // AfterReturningAdvice  
    9.     }  
    10.     catch (Throwable e) {  
    11.         // AfterThrowingAdvice  
    12.     }  
    13.     finally {  
    14.         // AfterAdvice  
    15.     }  
    16.     return retVal;  
    17. }  
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // BeforeAdvice
            Object retVal = null;
            try {
                // AroundAdvice
                retVal = method.invoke(target, args);
                // AroundAdvice
                // AfterReturningAdvice
            }
            catch (Throwable e) {
                // AfterThrowingAdvice
            }
            finally {
                // AfterAdvice
            }
            return retVal;
        }
    上面是对于Spring AOP使用JDK动态代理实现的基本框架代码,当然具体的实现肯定比这个复杂得多,但是基本原理不外乎如是。所以理解基本原理对于理解其他的代码也是很有好处的。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值