原文: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。下面来看看如何使用。

被代理的接口

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. * @author Mikan
  4. * @date 2015-09-15 18:01
  5. */
  6. public class HelloWorldImpl implements HelloWorld {
  7. @Override
  8. public void sayHello(String name) {
  9. System.out.println("Hello " + name);
  10. }
  11. }

实现一个java.lang.reflect.InvocationHandler:

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

使用代理:

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

运行的输出结果:

  1. localhost:classes mikan$ java com/mikan/proxy/ProxyTest
  2. Before invocation
  3. Hello Mikan
  4. 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. final Class<?>[] intfs = interfaces.clone();
  9. final SecurityManager sm = System.getSecurityManager();
  10. if (sm != null) {
  11. checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
  12. }
  13. // 这里是生成class的地方
  14. Class<?> cl = getProxyClass0(loader, intfs);
  15. // 使用我们实现的InvocationHandler作为参数调用构造方法来获得代理类的实例
  16. try {
  17. final Constructor<?> cons = cl.getConstructor(constructorParams);
  18. final InvocationHandler ih = h;
  19. if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
  20. return AccessController.doPrivileged(new PrivilegedAction<Object>() {
  21. public Object run() {
  22. return newInstance(cons, ih);
  23. }
  24. });
  25. } else {
  26. return newInstance(cons, ih);
  27. }
  28. } catch (NoSuchMethodException e) {
  29. throw new InternalError(e.toString());
  30. }
  31. }

其中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. }

其中代理缓存是使用WeakCache实现的,如下

  1. private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
  2. 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";
  5. // 用于生成代理类名字的计数器
  6. private static final AtomicLong nextUniqueNumber = new AtomicLong();
  7. @Override
  8. public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
  9. // 省略验证代理接口的代码……
  10. String proxyPkg = null;     // 生成的代理类的包名
  11. // 对于非公共接口,代理类的包名与接口的相同
  12. for (Class<?> intf : interfaces) {
  13. int flags = intf.getModifiers();
  14. if (!Modifier.isPublic(flags)) {
  15. String name = intf.getName();
  16. int n = name.lastIndexOf('.');
  17. String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
  18. if (proxyPkg == null) {
  19. proxyPkg = pkg;
  20. } else if (!pkg.equals(proxyPkg)) {
  21. throw new IllegalArgumentException(
  22. "non-public interfaces from different packages");
  23. }
  24. }
  25. }
  26. // 对于公共接口的包名,默认为com.sun.proxy
  27. if (proxyPkg == null) {
  28. proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
  29. }
  30. // 获取计数
  31. long num = nextUniqueNumber.getAndIncrement();
  32. // 默认情况下,代理类的完全限定名为:com.sun.proxy.$Proxy0,com.sun.proxy.$Proxy1……依次递增
  33. String proxyName = proxyPkg + proxyClassNamePrefix + num;
  34. // 这里才是真正的生成代理类的字节码的地方
  35. byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
  36. proxyName, interfaces);
  37. try {
  38. // 根据二进制字节码返回相应的Class实例
  39. return defineClass0(loader, proxyName,
  40. proxyClassFile, 0, proxyClassFile.length);
  41. } catch (ClassFormatError e) {
  42. throw new IllegalArgumentException(e.toString());
  43. }
  44. }
  45. }

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. }

saveGeneratedFiles这个属性的值从哪里来呢:

  1. 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.class (No such file or directory)
  2. at sun.misc.ProxyGenerator$1.run(ProxyGenerator.java:336)
  3. at sun.misc.ProxyGenerator$1.run(ProxyGenerator.java:327)
  4. at java.security.AccessController.doPrivileged(Native Method)
  5. at sun.misc.ProxyGenerator.generateProxyClass(ProxyGenerator.java:326)
  6. at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:672)
  7. at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:592)
  8. at java.lang.reflect.WeakCache$Factory.get(WeakCache.java:244)
  9. at java.lang.reflect.WeakCache.get(WeakCache.java:141)
  10. at java.lang.reflect.Proxy.getProxyClass0(Proxy.java:455)
  11. at java.lang.reflect.Proxy.newProxyInstance(Proxy.java:738)
  12. at com.mikan.proxy.ProxyTest.main(ProxyTest.java:15)
  13. at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  14. at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
  15. at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  16. at java.lang.reflect.Method.invoke(Method.java:606)
  17. 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. import com.mikan.proxy.HelloWorld;
  3. import java.lang.reflect.InvocationHandler;
  4. import java.lang.reflect.Method;
  5. import java.lang.reflect.Proxy;
  6. import java.lang.reflect.UndeclaredThrowableException;
  7. public final class $Proxy0 extends Proxy implements HelloWorld {
  8. private static Method m1;
  9. private static Method m3;
  10. private static Method m0;
  11. private static Method m2;
  12. public $Proxy0(InvocationHandler paramInvocationHandler) {
  13. super(paramInvocationHandler);
  14. }
  15. public final boolean equals(Object paramObject) {
  16. try {
  17. return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
  18. }
  19. catch (Error|RuntimeException localError) {
  20. throw localError;
  21. }
  22. catch (Throwable localThrowable) {
  23. throw new UndeclaredThrowableException(localThrowable);
  24. }
  25. }
  26. public final void sayHello(String paramString) {
  27. try {
  28. this.h.invoke(this, m3, new Object[] { paramString });
  29. return;
  30. }
  31. catch (Error|RuntimeException localError) {
  32. throw localError;
  33. }
  34. catch (Throwable localThrowable) {
  35. throw new UndeclaredThrowableException(localThrowable);
  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. public final String toString() {
  50. try {
  51. return (String)this.h.invoke(this, m2, null);
  52. }
  53. catch (Error|RuntimeException localError) {
  54. throw localError;
  55. }
  56. catch (Throwable localThrowable) {
  57. throw new UndeclaredThrowableException(localThrowable);
  58. }
  59. }
  60. static {
  61. try {
  62. m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
  63. m3 = Class.forName("com.mikan.proxy.HelloWorld").getMethod("sayHello", new Class[] { Class.forName("java.lang.String") });
  64. m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
  65. m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
  66. return;
  67. }
  68. catch (NoSuchMethodException localNoSuchMethodException) {
  69. throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
  70. }
  71. catch (ClassNotFoundException localClassNotFoundException) {
  72. throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
  73. }
  74. }
  75. }

可以看到,动态生成的代理类有如下特性:

  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. }

上面是对于Spring AOP使用JDK动态代理实现的基本框架代码,当然具体的实现肯定比这个复杂得多,但是基本原理不外乎如是。所以理解基本原理对于理解其他的代码也是很有好处的。

(转)细说JDK动态代理的实现原理的更多相关文章

  1. JDK动态代理的实现原理

    学习JDK动态代理,从源码层次来理解其实现原理参考:http://blog.csdn.net/jiankunking/article/details/52143504

  2. jdk动态代理使用及原理

    jdk动态代理的使用 1.创建实现InvocationHandler接口的类,实现invoke(Object proxy, Method method, Object[] args)接口,其中invo ...

  3. JDK动态代理案例与原理分析

    一.JDK动态代理实现案例 Person接口 package com.zhoucong.proxy.jdk; public interface Person { // 寻找真爱 void findlo ...

  4. jdk动态代理与cglib代理、spring aop代理实现原理

    原创声明:本博客来源与本人另一博客[http://blog.csdn.net/liaohaojian/article/details/63683317]原创作品,绝非他处摘取 代理(proxy)的定义 ...

  5. jdk动态代理与cglib代理、spring aop代理实现原理解析

    原创声明:本博客来源为本人原创作品,绝非他处摘取,转摘请联系博主 代理(proxy)的定义:为某对象提供代理服务,拥有操作代理对象的功能,在某些情况下,当客户不想或者不能直接引用另一个对象,而代理对象 ...

  6. 何为代理?jdk动态代理与cglib代理、spring Aop代理原理浅析

    原创声明:本博客来源为本人原创作品,绝非他处摘取,转摘请联系博主 代理(proxy)的定义:为某对象提供代理服务,拥有操作代理对象的功能,在某些情况下,当客户不想或者不能直接引用另一个对象,而代理对象 ...

  7. 代理模式(静态代理、JDK动态代理原理分析、CGLIB动态代理)

    代理模式 代理模式是设计模式之一,为一个对象提供一个替身或者占位符以控制对这个对象的访问,它给目标对象提供一个代理对象,由代理对象控制对目标对象的访问. 那么为什么要使用代理模式呢? 1.隔离,客户端 ...

  8. jdk动态代理与cglib代理、spring Aop代理原理-代理使用浅析

    原创声明:本博客来源为本人原创作品,绝非他处摘取,转摘请联系博主 代理(proxy)的定义:为某对象提供代理服务,拥有操作代理对象的功能,在某些情况下,当客户不想或者不能直接引用另一个对象,而代理对象 ...

  9. 解析JDK动态代理实现原理

    JDK动态代理使用实例 代理模式的类图如上.关于静态代理的示例网上有很多,在这里就不讲了. 因为本篇讲述要点是JDK动态代理的实现原理,直接从JDK动态代理实例开始. 首先是Subject接口类. p ...

随机推荐

  1. luogu P4744 [Wind Festival]Iron Man

    再次感谢题解区大佬的指点 规定\(pre[i]\)表示前缀\(i\)的前缀和,\(sum[i][j]\)表示区间\([i,j]\)之和 令\(f[i][j]\)表示前i个数选出j段的最大值,\(g[i ...

  2. 第19月第8天 斯坦福大学公开课机器学习 (吴恩达 Andrew Ng)

    1.斯坦福大学公开课机器学习 (吴恩达 Andrew Ng) http://open.163.com/special/opencourse/machinelearning.html 笔记 http:/ ...

  3. Oracle设置某张表为只读

    Oracle 11g开始支持设置某张表为只读,这样可以防范对某些表的修改,起到一定的安全性. 设置如下: --设置表为只读权限(加锁) ALTER TABLE tab_name READ ONLY ; ...

  4. Python 入门基础19 --面向对象、封装

    2019.04.17 一.面向对象与面向过程 二.名称空间操作 三.类与对象的概念 四.语法 五.对象查找属性的顺序 2019.04.18 1.类与对象的所有概念:__init__方法 2.类的方法与 ...

  5. java 多线程四

    java 多线程一 java 多线程二 java 多线程三 java 多线程四 一个生产者,消费者的例子: import java.util.Stack; /** * Created by root ...

  6. SpringBoot整合Email(电子邮件服务)

    (1).导入starter依赖 <dependency> <groupId>org.springframework.boot</groupId> <artif ...

  7. PP图和QQ图

     一. QQ图      分位数图示法(Quantile Quantile Plot,简称 Q-Q 图)       统计学里Q-Q图(Q代表分位数)是一个概率图,用图形的方式比较两个概率分布,把他们 ...

  8. Elastic-Job-Lite分析——作业调度器 JobScheduler 的创建过程

    -----------------------------------1. 创建注册中心的对象----------------------------------------------------- ...

  9. 深入理解node.js异步编程:基础篇

    ###[本文是基础内容,大神请绕道,才疏学浅,难免纰漏,请各位轻喷] ##1. 概述 目前开源社区最火热的技术当属Node.js莫属了,作为使用Javascript为主要开发语言的服务器端编程技术和平 ...

  10. Python3学习笔记08-tuple

    元组与列表类似,不同之处在于元组的元素不能修改 元组使用小括号,列表使用方括号 元组创建很简单,只需要在括号中添加元素,并使用逗号隔开即可 tup1 = ('Google', 'Runoob', 19 ...