JDK動態代理是代理模式的一種,且只能代理接口。spring也有動態代理,稱爲CGLib,如今主要來看一下JDK動態代理是如何實現的?java
1、介紹程序員
JDK動態代理是有JDK提供的工具類Proxy實現的,動態代理類是在運行時生成指定接口的代理類,每一個代理實例(實現須要代理的接口)都有一個關聯的調用處理程序對象,此對象實現了InvocationHandler,最終的業務邏輯是在InvocationHandler實現類的invoke方法上。spring
也便是在invoke方法上能夠實現原方法中沒有的業務邏輯,至關於spring aop的@Before、@After等註解。緩存
2、樣例微信
(1)接口數據結構
public interface ProxySource { void test(); }
(2)實現類app
public class ProxySourceImpl implements ProxySource { @Override public void test() { System.out.println("原有業務邏輯"); } }
(3)實現InvocationHandler接口和invoke方法ide
static class MyHandler implements InvocationHandler { //須要代理的類 Object target; public MyHandler(Object target) { this.target = target; } /** * @param proxy 動態代理實例 * @param method 須要執行的方法 * @param args 方法中參數 * @return * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("do other things befor"); method.invoke(target, args); System.out.println("do other things after"); return null; } }
(4)利用Proxy實現代理類工具
//此參數設置是爲了保存生成代理類的字節碼文件 System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true"); ProxySource proxySource = (ProxySource) Proxy.newProxyInstance( ProxySourceImpl.class.getClassLoader(), ProxySourceImpl.class.getInterfaces(), new MyHandler(new ProxySourceImpl()) ); //執行方法 proxySource.test();
(5)方法調用結果源碼分析
能夠看到,在原有方法執行先後都執行了其餘代碼。
3、源碼分析(主要看Proxy.newProxyInstance方法,省略非核心代碼)
(1)newProxyInstance方法
Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) { final Class<?>[] intfs = interfaces.clone(); //獲取代理接口class Class<?> cl = getProxyClass0(loader, intfs); //獲取到class以後用反射獲取構造方法,而後建立代理類實例 try { final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; return cons.newInstance(new Object[]{h}); } catch (Exception e) { throw new InternalError(e.toString(), e); } }
由上述代碼可知,主要是經過getProxyClass0方法獲取到代理接口的class
(2)getProxyClass0方法
Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) { //若是已經有相應的字節碼文件,則之間返回,不然經過代理類工廠建立代理類 return proxyClassCache.get(loader, interfaces); }
而proxyClassCache又是什麼東東呢?
WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
proxyClassCache是一個WeakCache對象,可知這是一個緩存對象,這個類結構是經過ConcurrentHashMap實現的,
ConcurrentMap<Object, ConcurrentMap<Object, Supplier<V>>> map = new ConcurrentHashMap<>();
數據結構是(key,sub-key)->value
存的值也就是<ClassLoader,<interfaces,$Proxy.class>>
(3)get方法
public V get(K key, P parameter) { Object cacheKey = CacheKey.valueOf(key, refQueue); //根據classloader爲key查看緩存中是否已有 ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey); if (valuesMap == null) { ConcurrentMap<Object, Supplier<V>> oldValuesMap = map.putIfAbsent(cacheKey, valuesMap = new ConcurrentHashMap<>()); if (oldValuesMap != null) { valuesMap = oldValuesMap; } } //獲取到weakcache種的sub-key Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter)); //根據sub-key去當前類加載器下是否有該代理接口的字節碼 Supplier<V> supplier = valuesMap.get(subKey); Factory factory = null; while (true) { if (supplier != null) { //supplier是代理類工廠實例 V value = supplier.get(); if (value != null) { return value; } } //建立代理類工廠 if (factory == null) { factory = new Factory(key, parameter, subKey, valuesMap); } //將上述建立的代理類工廠直接賦值給supplier if (supplier == null) { supplier = valuesMap.putIfAbsent(subKey, factory); if (supplier == null) { supplier = factory; } } else { if (valuesMap.replace(subKey, supplier, factory)) { supplier = factory; } else { supplier = valuesMap.get(subKey); } } } }
這個supplier.get方法點進去,核心就是ProxyClassFactory的apply方法
(4)ProxyClassFactory的apply方法
Class<?> apply(ClassLoader loader, Class<?>[] interfaces) { for (Class<?> intf : interfaces) { Class<?> interfaceClass = null; try { //經過類權限定名反射獲取class interfaceClass = Class.forName(intf.getName(), false, loader); } catch (ClassNotFoundException e) { } //判斷是否能夠經過系統加載器加載 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"); } } long num = nextUniqueNumber.getAndIncrement(); //生成代理類的權限定名,例如com.sun.proxy.$Proxy0 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) { throw new IllegalArgumentException(e.toString()); } }
上述代碼中核心是生成字節碼,便是ProxyGenerator.generateProxyClass方法。
(5)ProxyGenerator.generateProxyClass方法。
public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) { ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2); //生成字節碼 final byte[] var4 = var3.generateClassFile(); //開頭的設置 if (saveGeneratedFiles) { //保存生成代理類的字節碼文件 } return var4; }
上述代碼中saveGeneratedFiles點進去是這樣的,也便是開頭樣例中的設置屬性。
private static final boolean saveGeneratedFiles = ((Boolean)AccessController .doPrivileged(new GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"))).booleanValue();
但主要仍是ProxyGenerator的generateClassFile這個方法。
其默認給代理類生成了hashcode、equals和toString方法,也限制了代理接口和字段都不能超過65535個。
如今來看一下保存的代理類字節碼文件是怎麼樣的(經過idea反編譯後)
public final class $Proxy0 extends Proxy implements ProxySource { private static Method m1; private static Method m3; private static Method m2; private static Method m0; public $Proxy0(InvocationHandler var1) throws { super(var1); } public final boolean equals(Object var1) throws { //代碼省略 } public final void test() throws { //h是invocationhandler,因此最後是執行invoke方法 super.h.invoke(this, m3, (Object[])null); } public final String toString() throws { //代碼省略 } public final int hashCode() throws { //代碼省略 } static { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m3 = Class.forName("com.liusy.lang.ProxySource").getMethod("test"); m2 = Class.forName("java.lang.Object").getMethod("toString"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } }
能夠看到,生成的$Proxy0繼承了Proxy,實現了我定義的接口ProxySource,裏面有四個方法,m0~m3,經過靜態代碼塊中根據類的全限定名和方法名反射獲取,而最後是執行InvocationHandler的invoke方法。
至此,JDK動態代理已經說完,但願對你有所幫助。
=======================================================
我是Liusy,一個喜歡健身的程序員。
歡迎關注微信公衆號【Liusy01】,一塊兒交流Java技術及健身,獲取更多幹貨。