原文: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;
-
-
-
-
-
-
public interface HelloWorld {
-
-
void sayHello(String name);
-
-
}
接口的實現類:
-
package com.mikan.proxy;
-
-
-
-
-
-
public class HelloWorldImpl implements HelloWorld {
-
@Override
-
public void sayHello(String name) {
-
System.out.println("Hello " + name);
-
}
-
}
實現一個java.lang.reflect.InvocationHandler:
-
package com.mikan.proxy;
-
-
import java.lang.reflect.InvocationHandler;
-
import java.lang.reflect.Method;
-
-
-
-
-
-
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;
-
}
-
}
使用代理:
-
package com.mikan.proxy;
-
-
import java.lang.reflect.Proxy;
-
-
-
-
-
-
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");
-
}
-
-
}
運行的輸出結果:
-
localhost:classes mikan$ java com/mikan/proxy/ProxyTest
-
Before invocation
-
Hello Mikan
-
After invocation
從上面可以看出,JDK的動態代理使用起來非常簡單,但是隻知道如何使用是不夠的,知其然,還需知其所以然。所以要想搞清楚它的實現,那麼得從源碼入手。這裏的源碼是1.7.0_79。首先來看看它是如何生成代理類的:
-
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<?> cl = getProxyClass0(loader, intfs);
-
-
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這個方法的實現:
-
private static Class<?> getProxyClass0(ClassLoader loader,
-
Class<?>... interfaces) {
-
-
if (interfaces.length > 65535) {
-
throw new IllegalArgumentException("interface limit exceeded");
-
}
-
-
return proxyClassCache.get(loader, interfaces);
-
}
其中代理緩存是使用WeakCache實現的,如下
-
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
-
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
具體的緩存邏輯這裏暫不關心,只需要關心ProxyClassFactory是如何生成代理類的,ProxyClassFactory是Proxy的一個靜態內部類,實現了WeakCache的內部接口BiFunction的apply方法:
-
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");
-
}
-
}
-
}
-
-
-
if (proxyPkg == null) {
-
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
-
}
-
-
-
long num = nextUniqueNumber.getAndIncrement();
-
-
String proxyName = proxyPkg + proxyClassNamePrefix + num;
-
-
-
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
-
proxyName, interfaces);
-
try {
-
-
return defineClass0(loader, proxyName,
-
proxyClassFile, 0, proxyClassFile.length);
-
} catch (ClassFormatError e) {
-
throw new IllegalArgumentException(e.toString());
-
}
-
}
-
}
ProxyGenerator是sun.misc包中的類,它沒有開源,但是可以反編譯來一探究竟:
-
public static byte[] generateProxyClass(final String var0, Class[] var1) {
-
ProxyGenerator var2 = new ProxyGenerator(var0, var1);
-
final byte[] var3 = var2.generateClassFile();
-
-
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這個屬性的值從哪裏來呢:
-
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異常。如:
-
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文件,如下所示:
-
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());
-
}
-
}
-
}
可以看到,動態生成的代理類有如下特性:
- 繼承了Proxy類,實現了代理的接口,由於java不能多繼承,這裏已經繼承了Proxy類了,不能再繼承其他的類,所以JDK的動態代理不支持對實現類的代理,只支持接口的代理。
- 提供了一個使用InvocationHandler作爲參數的構造方法。
- 生成靜態代碼塊來初始化接口中方法的Method對象,以及Object類的equals、hashCode、toString方法。
- 重寫了Object類的equals、hashCode、toString,它們都只是簡單的調用了InvocationHandler的invoke方法,即可以對其進行特殊的操作,也就是說JDK的動態代理還可以代理上述三個方法。
- 代理類實現代理接口的sayHello方法中,只是簡單的調用了InvocationHandler的invoke方法,我們可以在invoke方法中進行一些特殊操作,甚至不調用實現的方法,直接返回。
至此JDK動態代理的實現原理就分析的差不多了。同時我們可以想像一下Spring AOP提供的各種攔截該如何實現,就已經很明瞭了,如下所示:
-
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
-
-
Object retVal = null;
-
try {
-
-
retVal = method.invoke(target, args);
-
-
-
}
-
catch (Throwable e) {
-
-
}
-
finally {
-
-
}
-
return retVal;
-
}
上面是對於Spring AOP使用JDK動態代理實現的基本框架代碼,當然具體的實現肯定比這個複雜得多,但是基本原理不外乎如是。所以理解基本原理對於理解其他的代碼也是很有好處的。