先抛出一个问题,JDK的动态代理为什么不支持对实现类的代理,只支持接口的代理???

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

下面实现一个jdk动态代理的例子:

1.被代理的接口,编写一个接口HelloTest

  1. package com.proxy.test2;
  2. public interface HelloTest {
  3. void say(String name);
  4. }

2.HelloTestImpl 实现接口HelloTest

  1. package com.proxy.test2;
  2. public class HelloTestImpl implements HelloTest {
  3. @Override
  4. public void say(String name) {
  5. System.out.println("Hello:"+name);
  6. }
  7. }

3.JDK的动态代码需要实现InvocationHandler

  1. package com.proxy.test2;
  2. import java.lang.reflect.InvocationHandler;
  3. import java.lang.reflect.Method;
  4. public class CustomInvocationHandler implements InvocationHandler {
  5. private Object target;
  6. public CustomInvocationHandler(Object target) {
  7. this.target = target;
  8. }
  9. @Override
  10. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  11. System.out.println("Before invocation");
  12. Object retVal = method.invoke(target, args);
  13. System.out.println("After invocation");
  14. return retVal;
  15. }
  16. }

4.编写一个测试类ProxyTest

  1. package com.proxy.test2;
  2. import java.lang.reflect.Proxy;
  3. public class ProxyTest {
  4. public static void main(String[] args) {
  5. //设置为true,会在工程根目录生成$Proxy0.class代理类(com.sun.proxy.$Proxy0.class)
  6. System.getProperties().put(
  7. "sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
  8. String saveGeneratedFiles = System.getProperty("sun.misc.ProxyGenerator.saveGeneratedFiles");
  9. System.out.println(saveGeneratedFiles);
  10. HelloTest helloWord = new HelloTestImpl();
  11. CustomInvocationHandler customInvocationHandler = new CustomInvocationHandler(
  12. helloWord);
  13. //通过Proxy.newProxyInstance生成代理对象
  14. HelloTest proxy = (HelloTest) Proxy.newProxyInstance(
  15. HelloTest.class.getClassLoader(),
  16. helloWord.getClass().getInterfaces(), customInvocationHandler);
  17. //调用say方法
  18. proxy.say("test");
  19. }
  20. }

5.运行测试类

  1. true
  2. Before invocation
  3. Hello:test
  4. After invocation

以上5步编写了一个JDK动态代理的例子,到底是如果代理的呢??

首先看ProxyTest 类中

HelloTest proxy = (HelloTest) Proxy.newProxyInstance(
HelloTest.class.getClassLoader(),
helloWord.getClass().getInterfaces(), customInvocationHandler);

查看Proxy.newProxyInstance源码:(JDK版本为jdk1.7.0_80)

  1. @CallerSensitive
  2. public static Object newProxyInstance(ClassLoader loader,
  3. Class<?>[] interfaces,
  4. InvocationHandler h)
  5. throws IllegalArgumentException
  6. {
  7. if (h == null) {
  8. throw new NullPointerException();
  9. }
  10. final Class<?>[] intfs = interfaces.clone();
  11. final SecurityManager sm = System.getSecurityManager();
  12. if (sm != null) {
  13. checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
  14. }
  15. /*
  16. * Look up or generate the designated proxy class.
  17. */
  18. //动态生成class的地方,重点是看这里面的方法
  19. Class<?> cl = getProxyClass0(loader, intfs);
  20. /*
  21. * Invoke its constructor with the designated invocation handler.
  22. */
  23. //获取代理类的实例
  24. try {
  25. final Constructor<?> cons = cl.getConstructor(constructorParams);
  26. final InvocationHandler ih = h;
  27. if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
  28. // create proxy instance with doPrivilege as the proxy class may
  29. // implement non-public interfaces that requires a special permission
  30. return AccessController.doPrivileged(new PrivilegedAction<Object>() {
  31. public Object run() {
  32. return newInstance(cons, ih);
  33. }
  34. });
  35. } else {
  36. return newInstance(cons, ih);
  37. }
  38. } catch (NoSuchMethodException e) {
  39. throw new InternalError(e.toString());
  40. }
  41. }

查看getProxyClass0方法

  1. /**
  2. * Generate a proxy class. Must call the checkProxyAccess method
  3. * to perform permission checks before calling this.
  4. */
  5. private static Class<?> getProxyClass0(ClassLoader loader,
  6. Class<?>... interfaces) {
  7. if (interfaces.length > 65535) {//
  8. throw new IllegalArgumentException("interface limit exceeded");
  9. }
  10. // If the proxy class defined by the given loader implementing
  11. // the given interfaces exists, this will simply return the cached copy;
  12. // otherwise, it will create the proxy class via the ProxyClassFactory
  13. // JDK对代理进行了缓存,如果已经存在相应的代理类,则直接返回,否则才会通过ProxyClassFactory来创建代理
  14. return proxyClassCache.get(loader, interfaces);
  15. }

点击proxyClassCache

  1. /**
  2. * a cache of proxy classes
  3. */
  4. private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
  5. proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

点击ProxyClassFactory

  1. /**
  2. * A factory function that generates, defines and returns the proxy class given
  3. * the ClassLoader and array of interfaces.
  4. */
  5. private static final class ProxyClassFactory
  6. implements BiFunction<ClassLoader, Class<?>[], Class<?>>
  7. // 所有代理类名字的前缀
  8. private static final String proxyClassNamePrefix = "$Proxy";
  9. ///用于生成唯一代理类名称的下一个数字
  10. private static final AtomicLong nextUniqueNumber = new AtomicLong();
  11. @Override
  12. public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
  13. Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
  14. for (Class<?> intf : interfaces) {
  15. /*
  16. * Verify that the class loader resolves the name of this
  17. * interface to the same Class object.
  18. */
  19. Class<?> interfaceClass = null;
  20. try {
  21. interfaceClass = Class.forName(intf.getName(), false, loader);
  22. } catch (ClassNotFoundException e) {
  23. }
  24. if (interfaceClass != intf) {
  25. throw new IllegalArgumentException(
  26. intf + " is not visible from class loader");
  27. }
  28. /*
  29. * Verify that the Class object actually represents an
  30. * interface.
  31. */
  32. if (!interfaceClass.isInterface()) {
  33. throw new IllegalArgumentException(
  34. interfaceClass.getName() + " is not an interface");
  35. }
  36. /*
  37. * Verify that this interface is not a duplicate.
  38. */
  39. if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
  40. throw new IllegalArgumentException(
  41. "repeated interface: " + interfaceClass.getName());
  42. }
  43. }
  44. String proxyPkg = null; // package to define proxy class in
  45. /*
  46. * Record the package of a non-public proxy interface so that the
  47. * proxy class will be defined in the same package. Verify that
  48. * all non-public proxy interfaces are in the same package.
  49. */
  50. for (Class<?> intf : interfaces) {
  51. int flags = intf.getModifiers();
  52. if (!Modifier.isPublic(flags)) {
  53. String name = intf.getName();
  54. int n = name.lastIndexOf('.');
  55. String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
  56. if (proxyPkg == null) {
  57. proxyPkg = pkg;
  58. } else if (!pkg.equals(proxyPkg)) {
  59. throw new IllegalArgumentException(
  60. "non-public interfaces from different packages");
  61. }
  62. }
  63. }
  64. if (proxyPkg == null) {
  65. // 如果没有公开的代理接口,使用com.sun.proxy包
  66. proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
  67. }
  68. /*
  69. * Choose a name for the proxy class to generate.
  70. */
  71. long num = nextUniqueNumber.getAndIncrement();
  72. /默认情况下,代理类的完全限定名为:com.sun.proxy.$Proxy0com.sun.proxy.$Proxy1……依次递增
  73. String proxyName = proxyPkg + proxyClassNamePrefix + num;
  74. /*
  75. * Generate the specified proxy class.
  76. */
  77. /真正的生成代理类的字节码的地方/
  78. byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
  79. proxyName, interfaces);
  80. try {
  81. // 根据二进制字节码返回相应的Class实例
  82. return defineClass0(loader, proxyName,
  83. proxyClassFile, 0, proxyClassFile.length);
  84. } catch (ClassFormatError e) {
  85. /*
  86. * A ClassFormatError here means that (barring bugs in the
  87. * proxy class generation code) there was some other
  88. * invalid aspect of the arguments supplied to the proxy
  89. * class creation (such as virtual machine limitations
  90. * exceeded).
  91. */
  92. throw new IllegalArgumentException(e.toString());
  93. }
  94. }
  95. }

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

在前面的测试类ProxyTest中,我例子中

//设置为true,会在工程根目录生成$Proxy0.class代理类(com.sun.proxy.$Proxy0.class)
System.getProperties().put(
"sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

反编译$Proxy0.class

  1. package com.sun.proxy;
  2. import com.proxy.test2.HelloTest;
  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
  8. implements HelloTest
  9. {
  10. private static Method m1;
  11. private static Method m0;
  12. private static Method m3;
  13. private static Method m2;
  14. public $Proxy0(InvocationHandler paramInvocationHandler)
  15. throws
  16. {
  17. super(paramInvocationHandler);
  18. }
  19. public final boolean equals(Object paramObject)
  20. throws
  21. {
  22. try
  23. {
  24. return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
  25. }
  26. catch (RuntimeException localRuntimeException)
  27. {
  28. throw localRuntimeException;
  29. }
  30. catch (Throwable localThrowable)
  31. {
  32. }
  33. throw new UndeclaredThrowableException(localThrowable);
  34. }
  35. public final int hashCode()
  36. throws
  37. {
  38. try
  39. {
  40. return ((Integer)this.h.invoke(this, m0, null)).intValue();
  41. }
  42. catch (RuntimeException localRuntimeException)
  43. {
  44. throw localRuntimeException;
  45. }
  46. catch (Throwable localThrowable)
  47. {
  48. }
  49. throw new UndeclaredThrowableException(localThrowable);
  50. }
  51. public final void say(String paramString)
  52. throws
  53. {
  54. try
  55. {
  56. this.h.invoke(this, m3, new Object[] { paramString });
  57. return;
  58. }
  59. catch (RuntimeException localRuntimeException)
  60. {
  61. throw localRuntimeException;
  62. }
  63. catch (Throwable localThrowable)
  64. {
  65. }
  66. throw new UndeclaredThrowableException(localThrowable);
  67. }
  68. public final String toString()
  69. throws
  70. {
  71. try
  72. {
  73. return (String)this.h.invoke(this, m2, null);
  74. }
  75. catch (RuntimeException localRuntimeException)
  76. {
  77. throw localRuntimeException;
  78. }
  79. catch (Throwable localThrowable)
  80. {
  81. }
  82. throw new UndeclaredThrowableException(localThrowable);
  83. }
  84. static
  85. {
  86. try
  87. {
  88. m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
  89. m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
  90. m3 = Class.forName("com.proxy.test2.HelloTest").getMethod("say", new Class[] { Class.forName("java.lang.String") });
  91. m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
  92. return;
  93. }
  94. catch (NoSuchMethodException localNoSuchMethodException)
  95. {
  96. throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
  97. }
  98. catch (ClassNotFoundException localClassNotFoundException)
  99. {
  100. }
  101. throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
  102. }
  103. }

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

  1. 继承了Proxy类,实现了代理的接口,由于java不能多继承,这里已经继承了Proxy类了,不能再继承其他的类,所以JDK的动态代理不支持对实现类的代理,只支持接口的代理。
  2. 提供了一个使用InvocationHandler作为参数的构造方法。
  3. 生成静态代码块来初始化接口中方法的Method对象,以及Object类的equals、hashCode、toString方法。
  4. 重写了Object类的equals、hashCode、toString,它们都只是简单的调用了InvocationHandler的invoke方法,即可以对其进行特殊的操作,也就是说JDK的动态代理还可以代理上述三个方法。
  5. 代理类实现代理接口的say方法中,只是简单的调用了InvocationHandler的invoke方法,我们可以在invoke方法中进行一些特殊操作,甚至不调用实现的方法,直接返回。

JDK动态代理源码分析的更多相关文章

  1. 动态代理学习(二)JDK动态代理源码分析

    上篇文章我们学习了如何自己实现一个动态代理,这篇文章我们从源码角度来分析下JDK的动态代理 先看一个Demo: public class MyInvocationHandler implements ...

  2. jdk 动态代理源码分析

    闲来无事,撸撸源码 使用方法 直接看代码吧.. package com.test.demo.proxy; import java.lang.reflect.InvocationHandler; imp ...

  3. 设计模式之JDK动态代理源码分析

    这里查看JDK1.8.0_65的源码,通过debug学习JDK动态代理的实现原理 大概流程 1.为接口创建代理类的字节码文件 2.使用ClassLoader将字节码文件加载到JVM 3.创建代理类实例 ...

  4. java 1.8 动态代理源码分析

    JDK8动态代理源码分析 动态代理的基本使用就不详细介绍了: 例子: class proxyed implements pro{ @Override public void text() { Syst ...

  5. JDK动态代理源码学习

    继上一篇博客设计模式之代理模式学习之后http://blog.csdn.net/u014427391/article/details/75115928,本博客介绍JDK动态代理的实现原理,学习一下JD ...

  6. JDK7动态代理源码分析

    IObject proxy = (IObject) Proxy.newProxyInstance(IObject.class.getClassLoader(), new Class[]{IObject ...

  7. JDK动态代理源码解析

    动态代理.静态代理优缺点     关于JDK的动态代理,最为人熟知的可能要数Spring AOP的实现,默认情况下,Spring AOP的实现对于接口来说就是使用的JDK的动态代理来实现的,而对于类的 ...

  8. 深入剖析JDK动态代理源码实现

    动态代理.静态代理优缺点优点:业务类只需要关注业务逻辑本身,保证了业务类的重用性.这是代理的共有优点.动态代理只有在用到被代理对象的时候才会对被代理类进行类加载. 而静态代理在编译器就已经开始占内存了 ...

  9. JDK动态代理源码剖析

    关键代码: 1.Proxy.newInstance: private static final Class<?>[] constructorParams = { InvocationHan ...

随机推荐

  1. P4001 [BJOI2006]狼抓兔子

    传送门 思路: 不少题解都是用网络流来做最小割(网络流是什么),但对于一个不会网络流的蒟蒻来做这题相当困难. 听机房daolao说可以重构图做最短路.然后就baidu将平面图转换成一个对偶图,因为网络 ...

  2. CentOS下修改Apache默认端口80

    打开  /etc/httpd/conf/httpd.conf  文件 修改这个地方     #Listen 12.34.56.78:80     Listen 80 #把80改为你设置的端口,我设置端 ...

  3. mac 下安装ES 与 Head插件 以及安装Kibana

    一.安装Elasticsearch 在Mac上可以使用brew快速安装Elasticsearch brew install elasticsearch 安装完成后可使用elasticsearch -- ...

  4. tensorflow学习笔记一------下载安装,配置环境(基于ubuntu16.04 pycharm)

    一,安装TensorFlow 安装所有必备工具 sudo apt-get install python-pip python-dev python-virtualenv 安装TensorFlow su ...

  5. Unity中角度与弧度之间的相互转换

    弧度数 = 角度数 * Mathf.Deg2Rad角度数 = 弧度数 * Mathf.Rad2Deg

  6. lua --- 用break实现continue逻辑

    循环中内嵌一个循环,然后将具体的逻辑放在内嵌循环中去处理,在内嵌循环的开始,添加一个判断语句,满足条件就跳出内嵌循环. 示例代码如下: tab = {,,,,} ,#tab do while true ...

  7. Unity--- 资源路径问题

    使用 System.IO.Path 这个API得到的路径,其实也是以"\"分隔路径的. 我们在windows下打开资源管理器,某个目录或文件的路径为:E:\uniuProject5 ...

  8. Linux 各种软件的安装 - svn

    首先感谢这篇博文 https://www.cnblogs.com/mymelon/p/5483215.html 按照他的配置,svn顺利搭好. 1:yum -y install subversion ...

  9. missing seperator error when [make all]

    https://stackoverflow.com/questions/16931770/makefile4-missing-separator-stop makefile has a very st ...

  10. 非常好的 gdb tui 的文章

    http://beej.us/guide/bggdb/ Help Commands help command Get help on a certain command apropos keyword ...