什麼是jdk動態代理
JDK的動態代理,就是在程序運行的過程中,根據被代理的接口來動態生成代理類的class文件,並加載運行的過程。JDK從1.3開始支持動態代理。那麼JDK是如何生成動態代理的呢?JDK動態代理爲什麼不支持類的代理,只支持接口的代理?
首先來看一下如何使用JDK動態代理。JDK提供了java.lang.reflect.Proxy類來實現動態代理的,可通過它的newProxyInstance來獲得代理實現類。同時對於代理的接口的實際處理,是一個java.lang.reflect.InvocationHandler,它提供了一個invoke方法供實現者提供相應的代理邏輯的實現。可以對實際的實現進行一些特殊的處理,像Spring AOP中的各種advice。
簡單使用
1.定義被代理接口
public interface HelloWorld { void sayHello(String name); }
2.接口實現類
public class HelloWorldImpl implements HelloWorld{ @Override public void sayHello(String name) { System.out.println("hello"+name); } }
3.實現一個java.lang.reflect.InvocationHandler:對方法的增強
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; } }
4.使用代理
public class ProxyTest { public static void main(String[] args) { 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"); } }
5.輸出
Before invocation
helloMikan
After invocation
原理
<1>
@CallerSensitive public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { Objects.requireNonNull(h); //clone代理接口 class對象 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 { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } // 有了代理類的實例 使用我們實現的InvocationHandler作爲參數調用構造方法來獲得代理類的實例 final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { cons.setAccessible(true); return null; } }); } //構造函數創建<2> return cons.newInstance(new Object[]{h}); } catch (IllegalAccessException|InstantiationException e) { throw new InternalError(e.toString(), e); } catch (InvocationTargetException e) { Throwable t = e.getCause(); if (t instanceof RuntimeException) { throw (RuntimeException) t; } else { throw new InternalError(t.toString(), t); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString(), e); } }
<2>
//緩存是弱引用 每次gc後都會回收 具體創建看ProxyClassFactory<3> private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new Proxy.KeyFactory(), new Proxy.ProxyClassFactory()); 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); }
<3>
//Proxy內部靜態類 private static final class ProxyClassFactory implements BiFunction<ClassLoader, Class<?>[], Class<?>> { // 所有代理類名字的前綴 如class com.sun.proxy.$Proxy0 private static final String proxyClassNamePrefix = "$Proxy"; // // 用於生成代理類名字的計數器 private static final AtomicLong nextUniqueNumber = new AtomicLong(); @Override public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) { Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length); for (Class<?> intf : interfaces) { Class<?> interfaceClass = null; try { //根據classLoader獲取被代理接口的class interfaceClass = Class.forName(intf.getName(), false, loader); } catch (ClassNotFoundException e) { } //不相等表示傳入的classLoader和被代理接口不一致校驗失敗 if (interfaceClass != intf) { throw new IllegalArgumentException( intf + " is not visible from class loader"); } //非接口類型報錯 if (!interfaceClass.isInterface()) { throw new IllegalArgumentException( interfaceClass.getName() + " is not an interface"); } /* * 避免重複定義被代理接口 如果從夫定義報錯 */ if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) { throw new IllegalArgumentException( "repeated interface: " + interfaceClass.getName()); } } String proxyPkg = null; //存儲代理類的包名 int accessFlags = Modifier.PUBLIC | Modifier.FINAL; // 對於非公共接口,代理類的包名與接口的相同 for (Class<?> intf : interfaces) { int flags = intf.getModifiers(); if (!Modifier.isPublic(flags)) { accessFlags = Modifier.FINAL; 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) { // if no non-public proxy interfaces, use com.sun.proxy package proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; } /* * 計數器+1 */ long num = nextUniqueNumber.getAndIncrement(); //獲得代理類的全名稱 String proxyName = proxyPkg + proxyClassNamePrefix + num; /* * 這裏纔是真正的生成代理類的字節碼的地方 */ byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); try { //根據二進制字節碼返回相應的Class實例 return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { /* * A ClassFormatError here means that (barring bugs in the * proxy class generation code) there was some other * invalid aspect of the arguments supplied to the proxy * class creation (such as virtual machine limitations * exceeded). */ 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(); // 這裏根據參數配置,決定是否把生成的字節碼(.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這個屬性的值從哪裏來呢:
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方法中進行一些特殊操作,甚至不調用實現的方法,直接返回。