关于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。下面来看看如何使用。
被代理的接口
[java]
view plain
copy
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. }
接口的实现类:
[java]
view plain
copy
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. "Hello " + name);
11. }
12. }
实现一个java.lang.reflect.InvocationHandler:
[java]
view plain
copy
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. "Before invocation");
20. Object retVal = method.invoke(target, args);
21. "After invocation");
22. return retVal;
23. }
24. }
使用代理:
[java]
view plain
copy
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. "sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
13.
14. new CustomInvocationHandler(new HelloWorldImpl());
15. HelloWorld proxy = (HelloWorld) Proxy.newProxyInstance(
16. class.getClassLoader(),
17. new Class[]{HelloWorld.class},
18. handler);
19. "Mikan");
20. }
21.
22. }
运行的输出结果:
[plain]
view plain
copy
1. localhost:classes mikan$ java com/mikan/proxy/ProxyTest
2. Before invocation
3. Hello Mikan
4. After invocation
从上面可以看出,JDK的动态代理使用起来非常简单,但是只知道如何使用是不够的,知其然,还需知其所以然。所以要想搞清楚它的实现,那么得从源码入手。这里的源码是1.7.0_79。首先来看看它是如何生成代理类的:
[java]
view plain
copy
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. }
其中newInstance只是调用Constructor.newInstance来构造相应的代理类实例,这里重点是看getProxyClass0这个方法的实现:
[java]
view plain
copy
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实现的,如下
[java]
view plain
copy
1. private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
2. new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
具体的缓存逻辑这里暂不关心,只需要关心ProxyClassFactory是如何生成代理类的,ProxyClassFactory是Proxy的一个静态内部类,实现了WeakCache的内部接口BiFunction的apply方法:
[java]
view plain
copy
1. private static final class ProxyClassFactory
2. implements BiFunction<ClassLoader, Class<?>[], Class<?>> {
3. // 所有代理类名字的前缀
4. private static final String proxyClassNamePrefix = "$Proxy";
5.
6. // 用于生成代理类名字的计数器
7. private static final AtomicLong nextUniqueNumber = new AtomicLong();
8.
9. @Override
10. public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
11. // 省略验证代理接口的代码……
12.
13. null; // 生成的代理类的包名
14. // 对于非公共接口,代理类的包名与接口的相同
15. for (Class<?> intf : interfaces) {
16. int flags = intf.getModifiers();
17. if (!Modifier.isPublic(flags)) {
18. String name = intf.getName();
19. int n = name.lastIndexOf('.');
20. 1) ? "" : name.substring(0, n + 1));
21. if (proxyPkg == null) {
22. proxyPkg = pkg;
23. else if (!pkg.equals(proxyPkg)) {
24. throw new IllegalArgumentException(
25. "non-public interfaces from different packages");
26. }
27. }
28. }
29.
30. // 对于公共接口的包名,默认为com.sun.proxy
31. if (proxyPkg == null) {
32. ".";
33. }
34.
35. // 获取计数
36. long num = nextUniqueNumber.getAndIncrement();
37. // 默认情况下,代理类的完全限定名为:com.sun.proxy.$Proxy0,com.sun.proxy.$Proxy1……依次递增
38. String proxyName = proxyPkg + proxyClassNamePrefix + num;
39.
40. // 这里才是真正的生成代理类的字节码的地方
41. byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
42. proxyName, interfaces);
43. try {
44. // 根据二进制字节码返回相应的Class实例
45. return defineClass0(loader, proxyName,
46. 0, proxyClassFile.length);
47. catch (ClassFormatError e) {
48. throw new IllegalArgumentException(e.toString());
49. }
50. }
51. }
ProxyGenerator是sun.misc包中的类,它没有开源,但是可以反编译来一探究竟:
[java]
view plain
copy
1. public static byte[] generateProxyClass(final String var0, Class[] var1) {
2. new ProxyGenerator(var0, var1);
3. final byte[] var3 = var2.generateClassFile();
4. // 这里根据参数配置,决定是否把生成的字节码(.class文件)保存到本地磁盘,我们可以通过把相应的class文件保存到本地,再反编译来看看具体的实现,这样更直观
5. if(saveGeneratedFiles) {
6. new PrivilegedAction() {
7. public Void run() {
8. try {
9. 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这个属性的值从哪里来呢:
[java]
view plain
copy
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异常。如:
[java]
view plain
copy
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文件,如下所示:
[java]
view plain
copy
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 extends Proxy implements HelloWorld {
10. private static Method m1;
11. private static Method m3;
12. private static Method m0;
13. private static Method m2;
14.
15. public $Proxy0(InvocationHandler paramInvocationHandler) {
16. super(paramInvocationHandler);
17. }
18.
19. public final boolean equals(Object paramObject) {
20. try {
21. return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
22. }
23. catch (Error|RuntimeException localError) {
24. throw localError;
25. }
26. catch (Throwable localThrowable) {
27. throw new UndeclaredThrowableException(localThrowable);
28. }
29. }
30.
31. public final void sayHello(String paramString) {
32. try {
33. this.h.invoke(this, m3, new Object[] { paramString });
34. return;
35. }
36. catch (Error|RuntimeException localError) {
37. throw localError;
38. }
39. catch (Throwable localThrowable) {
40. throw new UndeclaredThrowableException(localThrowable);
41. }
42. }
43.
44. public final int hashCode() {
45. try {
46. return ((Integer)this.h.invoke(this, m0, null)).intValue();
47. }
48. catch (Error|RuntimeException localError) {
49. throw localError;
50. }
51. catch (Throwable localThrowable) {
52. throw new UndeclaredThrowableException(localThrowable);
53. }
54. }
55.
56. public final String toString() {
57. try {
58. return (String)this.h.invoke(this, m2, null);
59. }
60. catch (Error|RuntimeException localError) {
61. throw localError;
62. }
63. catch (Throwable localThrowable) {
64. throw new UndeclaredThrowableException(localThrowable);
65. }
66. }
67.
68. static {
69. try {
70. "java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
71. "com.mikan.proxy.HelloWorld").getMethod("sayHello", new Class[] { Class.forName("java.lang.String") });
72. "java.lang.Object").getMethod("hashCode", new Class[0]);
73. "java.lang.Object").getMethod("toString", new Class[0]);
74. return;
75. }
76. catch (NoSuchMethodException localNoSuchMethodException) {
77. throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
78. }
79. catch (ClassNotFoundException localClassNotFoundException) {
80. throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
81. }
82. }
83. }
可以看到,动态生成的代理类有如下特性:
- 继承了Proxy类,实现了代理的接口,由于java不能多继承,这里已经继承了Proxy类了,不能再继承其他的类,所以JDK的动态代理不支持对实现类的代理,只支持接口的代理。
- 提供了一个使用InvocationHandler作为参数的构造方法。
- 生成静态代码块来初始化接口中方法的Method对象,以及Object类的equals、hashCode、toString方法。
- 重写了Object类的equals、hashCode、toString,它们都只是简单的调用了InvocationHandler的invoke方法,即可以对其进行特殊的操作,也就是说JDK的动态代理还可以代理上述三个方法。
- 代理类实现代理接口的sayHello方法中,只是简单的调用了InvocationHandler的invoke方法,我们可以在invoke方法中进行一些特殊操作,甚至不调用实现的方法,直接返回。
[java]
view plain
copy
1. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
2. // BeforeAdvice
3. 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动态代理实现的基本框架代码,当然具体的实现肯定比这个复杂得多,但是基本原理不外乎如是。所以理解基本原理对于理解其他的代码也是很有好处的。