在闡述JDK動態代理以前,咱們頗有必要先來弄明白代理的概念。代理這個詞自己並非計算機專用術語,它是生活中一個經常使用的概念。這裏引用維基百科上的一句話對代理進行定義:java
A proxy is an agent or substitute authorized to act for another person or a document which authorizes the agent so to act.程序員
意思是說:代理指的是一個代理人(或替代品),它被受權表明另一我的(或文檔)。api
從這個簡明扼要的定義中,能夠看出代理的一些特性:1.代理存在的意義就是表明另外一個事物。2.代理至少須要完成(或實現)它所表明的事物的功能。數組
JAVA靜態代理是指由程序員建立或工具生成的代理類,這個類在編譯期就已是肯定了的,存在的。緩存
典型的靜態代理模式通常包含三類角色:安全
1.抽象角色:它的做用是定義一組行爲規範。抽象角色通常呈現爲接口(或抽象類),這些接口(或抽象類)中定義的方法就是待實現的。數據結構
2.真實角色:實現了抽象角色所定義的行爲。真實角色就是個普通的類,它須要實現抽象角色定義的那些接口。app
3.代理角色:表明真實角色的角色。根據上面代理的定義,咱們能夠知道代理角色須要至少完成(或實現)真實角色的功能。爲了完成這一使命,那麼代理角色也須要實現抽象角色所定義的行爲(即代理類須要實現抽象角色所定義的接口),而且在實現接口方法的時候須要調用真實角色的相應方法。 上圖使用UML類圖解釋了靜態代理的數據模型。ide
1.接口IFunc表明了抽象角色,定義了一個行爲,即方法doSomething()。函數
2.類RealFunc表明了真實角色,它實現了IFunc接口中定義的方法doSomething()。
3.類ProxyFunc表明了代理角色,它實現了IFunc接口中定義的方法doSomething()。它的實現方式是依賴RealFunc類的,經過持有RealFunc類對象的引用realObj,在ProxyFunc.doSomething()方法中調用了realObj.doSomething()。固然,代理類也能夠作一些其餘的事情,如圖中的doOtherthing()。
經過上面的介紹,能夠看出靜態代理存在如下問題:
1.代理類依賴於真實類,由於代理類最根本的業務功能是須要經過調用真實類來實現的。那麼若是事先不知道真實類,該如何使用代理模式呢?
2.一個真實類必須對應一個代理類,即當有多個真實類RealA、RealB、RealC...的時候,就須要多個代理類ProxyA、ProxyB、ProxyC...。這樣的話若是大量使用靜態代理,容易致使類的急劇膨脹。該如何解決?
要想解決上述問題,就須要使用下面講解的JAVA動態代理。
JAVA動態代理與靜態代理相對,靜態代理是在編譯期就已經肯定代理類和真實類的關係,而且生成代理類的。而動態代理是在運行期利用JVM的反射機制生成代理類,這裏是直接生成類的字節碼,而後經過類加載器載入JAVA虛擬機執行。如今主流的JAVA動態代理技術的實現有兩種:一種是JDK自帶的,就是咱們所說的JDK動態代理,另外一種是開源社區的一個開源項目CGLIB。本文主要對JDK動態代理作討論。
JDK動態代理的實現是在運行時,根據一組接口定義,使用Proxy、InvocationHandler等工具類去生成一個代理類和代理類實例。
JDK動態代理的類關係模型和靜態代理看起來差很少。也是須要一個或一組接口來定義行爲規範。須要一個代理類來實現接口。區別是沒有真實類,由於動態代理就是要解決在不知道真實類的狀況下依然可以使用代理模式的問題。 圖中高亮顯示的$Proxy0即爲JDK動態代理技術生成的代理類,類名的生成規則是前綴"$Proxy"加上一個序列數。這個類繼承Proxy,實現一系列的接口Intf1,Intf2...IntfN。
既然要實現接口,那麼就要實現接口的各個方法,即圖中的doSomething1(),doSomething2()...doSomethingN()。咱們上面介紹靜態代理的時候,知道靜態代理類本質上是調用真實類去實現接口定義的方法的。可是JDK動態代理中是沒有真實類這樣的概念的。那麼JDK動態代理類是如何實現這些接口方法的具體邏輯呢?答案就在圖中的InvocationHandler上。$Proxy0對外只提供一個構造函數,這個構造函數接受一個InvocationHandler實例h,這個構造函數的邏輯很是簡單,就是調用父類的構造函數,將參數h賦值給對象字段h。最終就是把全部的方法實現都分派到InvocationHandler實例h的invoke方法上。因此JDK動態代理的接口方法實現邏輯是徹底由InvocationHandler實例的invoke方法決定的。
瞭解了JDK動態代理的概念後,如今咱們動手寫個JDK動態代理的代碼樣例。直觀的認識下JDK動態代理技術爲咱們的作了什麼。上面說到了,JDK動態代理主要依靠Proxy和InvocationHandler這兩個類來生成動態代理類和類的實例。這兩個類都在jdk的反射包java.lang.reflect下面。Proxy是個工具類,有了它就能夠爲接口生成動態代理類了。若是須要進一步生成代理類實例,須要注入InvocationHandler實例。這點咱們上面解釋過,由於代理類最終邏輯的實現是分派給InvocationHandler實例的invoke方法的。
閒話休絮,先開始咱們的第一步,定義一個接口。這個接口裏面定義一個方法helloWorld()。
1 public interface MyIntf { 2 void helloWorld(); 3 }
第二步,編寫一個咱們本身的調用處理類,這個類須要實現InvocationHandler接口。
1 public class MyInvocationHandler implements InvocationHandler { 2 @Override 3 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 4 System.out.println(method); 5 return null; 6 } 7 }
InvocationHandler接口只有一個待實現的invoke方法。這個方法有三個參數,proxy表示動態代理類實例,method表示調用的方法,args表示調用方法的參數。在實際應用中,invoke方法就是咱們實現業務邏輯的入口。這裏咱們的實現邏輯就一行代碼,打印當前調用的方法(在實際應用中這麼作是沒有意義的,不過這裏咱們只想解釋JDK動態代理的原理,因此越簡單越清晰)。
第三步,直接使用Proxy提供的方法建立一個動態代理類實例。並調用代理類實例的helloWorld方法,檢測運行結果。
1 public class ProxyTest { 2 public static void main(String[] args) { 3 System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true"); 4 MyIntf proxyObj = (MyIntf)Proxy.newProxyInstance(MyIntf.class.getClassLoader(),new Class[]{MyIntf.class},new MyInvocationHandler()); 5 proxyObj.helloWorld(); 6 } 7 }
第三行代碼是設置系統屬性,把生成的代理類寫入到文件。這裏再強調一下,JDK動態代理技術是在運行時直接生成類的字節碼,並載入到虛擬機執行的。這裏不存在class文件的,因此咱們經過設置系統屬性,把生成的字節碼保存到文件,用於後面進一步分析。
第四行代碼就是調用Proxy.newProxyInstance方法建立一個動態代理類實例,這個方法須要傳入三個參數,第一個參數是類加載器,用於加載這個代理類。第二個參數是Class數組,裏面存放的是待實現的接口信息。第三個參數是InvocationHandler實例。
第五行調用代理類的helloWorld方法,運行結果:
public abstract void com.tuniu.distribute.openapi.common.annotation.MyIntf.helloWorld()
分析運行結果,就能夠發現,方法的最終調用是分派到了MyInvocationHandler.invoke方法,打印出了調用的方法信息。
到這裏,對於JDK動態代理的基本使用就算講完了。咱們作的事情不多,只是編寫了接口MyIntf和調用處理類MyInvocationHandler。其餘大部分的工做都是Proxy工具類幫咱們完成的。Proxy幫咱們建立了動態代理類和代理類實例。上面的代碼咱們設置了系統屬性,把生成的字節碼保存到class文件。下面咱們經過反編譯軟件(如jd-gui),看下Proxy類爲咱們生成的代理類是什麼樣子的。
1 package com.sun.proxy; 2 import com.tuniu.distribute.openapi.common.annotation.MyIntf; 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 8 public final class $Proxy0 extends Proxy implements MyIntf { 9 private static Method m0; 10 private static Method m1; 11 private static Method m2; 12 private static Method m3; 13 14 static { 15 try { 16 m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); 17 m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")}); 18 m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); 19 m3 = Class.forName("com.tuniu.distribute.openapi.common.annotation.MyIntf").getMethod("helloWorld", new Class[0]); 20 return; 21 } catch (NoSuchMethodException localNoSuchMethodException) { 22 throw new NoSuchMethodError(localNoSuchMethodException.getMessage()); 23 } catch (ClassNotFoundException localClassNotFoundException) { 24 throw new NoClassDefFoundError(localClassNotFoundException.getMessage()); 25 } 26 } 27 28 public $Proxy0(InvocationHandler paramInvocationHandler) { 29 super(paramInvocationHandler); 30 } 31 32 public final void helloWorld() { 33 try { 34 this.h.invoke(this, m3, null); 35 return; 36 } catch (Error | RuntimeException localError) { 37 throw localError; 38 } catch (Throwable localThrowable) { 39 throw new UndeclaredThrowableException(localThrowable); 40 } 41 } 42 43 // 後面省略equals(),hashCode(),toString()三個方法的代碼,由於這三個方法和helloWorld()方法很是類似 44 }
這裏Proxy爲咱們生成的代理類叫$Proxy0,繼承了Proxy,實現了咱們定義的接口MyIntf。每個JDK動態代理技術生成的代理類的名稱都是由$Proxy前綴加上一個序列數0,1,2...。而且都須要繼承Proxy類。
$Proxy0類中9-26行代碼定義了4個Method字段m0,m1,m2,m3,咱們先來看下m3,它描述了咱們定義的接口MyIntf中的方法helloWorld。
緊接着下面的32-41行代碼就是對helloWorld方法的實現,它的實現很是簡單就一句話this.h.invoke(this, m3, null);這行代碼就是調用當前對象的h實例的invoke方法,也就是把方法的實現邏輯分派給了h.invoke。這裏的h是繼承父類Proxy中的InvocationHandler字段(讀者能夠結合上面的動態代理類圖模型或者Proxy源碼進一步理解)。
同時$Proxy0提供了一個構造函數(代碼28-30行),調用父類的構造函數來注入這個InvocationHandler實例。
$Proxy0中的另外3個Method對象m0,m1,m2分別表明了Object類的hashCode(),equals(),toString()方法,咱們知道java中的全部類都是Object的子類(Object類自己除外),這裏$Proxy0重寫了Object中的這三個方法。這三個方法的實現和helloWorld方法很相似,因此筆者這裏就把這段代碼省略了,用一行註釋(43行代碼)解釋了下。
行文至此,咱們已經感官的認識了運行時生成的代理類結構。揭開了這層面紗,其實JDK動態代理也沒什麼了。簡單的來講就是,JDK動態代理技術能夠爲一組接口生成代理類,這個代理類也就是一層殼,簡單的實現了接口中定義的方法。經過提供一個構造函數傳入InvocationHandler實例,而後將方法的具體實現交給它。
前兩部分分別從概念和應用的角度闡述了JDK動態代理技術。最後一部分咱們將從Proxy源碼對JDK動態代理進行深刻的剖析。Proxy類對外提供了4個靜態方法,分別爲:
1 public static Class<?> getProxyClass(ClassLoader loader,Class<?>... interfaces); 2 public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h); 3 public static boolean isProxyClass(Class<?> cl); 4 public static InvocationHandler getInvocationHandler(Object proxy);
下面咱們經過各個方法的源碼依次分析。
getProxyClass方法返回代理類的Class實例。這個代理類就是類加載器loader定義的、實現了一些列接口interfaces的。若是以前已經爲這個loader和interfaces建立過代理類,那麼直接返回這個代理類的Class實例。若是沒有,則動態建立並返回。
1 public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) throws IllegalArgumentException { 2 final Class<?>[] intfs = interfaces.clone(); 3 final SecurityManager sm = System.getSecurityManager(); 4 if (sm != null) { 5 checkProxyAccess(Reflection.getCallerClass(), loader, intfs); 6 } 7 return getProxyClass0(loader, intfs); 8 }
getProxyClass方法並無JDK動態代理的核心邏輯:第二行將接口的Class數組interfaces進行克隆。3-6行是類加載器和接口訪問權限的校驗(這裏虛擬機的安全性相關邏輯,不是咱們JDK代理技術的關注點,因此不作過多解釋)。關鍵的邏輯就最後一行代碼,調用getProxyClass0方法去獲取代理類的Class實例。
1 private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) { 2 if (interfaces.length > 65535) { 3 throw new IllegalArgumentException("interface limit exceeded"); 4 } 5 return proxyClassCache.get(loader, interfaces); 6 }
getProxyClass0也沒有包含JDK動態代理的核心邏輯:2-4行只是對接口的個數進行了簡單的校驗,不能超過65535,咱們在實際應用中通常也不會出現這種狀況。最後一行代碼是去緩存對象proxyClassCache中獲取代理類的Class實例。proxyClassCache是Proxy類的靜態變量
private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
proxyClassCache是類java.lang.reflect.WeakCache的實例,經過類名就能夠看出來這個類是用來作緩存的。Proxy調用WeakCache提供的構造函數,傳入KeyFactory實例和ProxyClassFactory實例(這兩個實例的用途後面會講到)。在分析WeakCache的get方法源碼以前,咱們先來大概介紹下WeakCache緩存的數據結構。
WeakCache緩存的數據結構是(key,sub-key)->(value)。這個結構和Redis裏面的hash結構很相似,根據一級的鍵(key)、二級的鍵(sub-key)爲索引,去檢索出值(value)。對應到WeakCache類代碼裏面,就是一個ConcurrentMap實例map,這個map的key就是一級鍵,map的value又是個ConcurrentMap實例,這個子map的key是二級鍵,子map的value就是緩存的的值。上面圖中的箭頭就表示着對應關係,一目瞭然。圖中下半部分是JDK動態代理緩存的鍵值生成規則,後面會一一詳解。下面咱們看下WeakCache的get方法源碼。
1 public V get(K key, P parameter) { 2 Objects.requireNonNull(parameter); 3 expungeStaleEntries(); 4 Object cacheKey = CacheKey.valueOf(key, refQueue); 5 ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey); 6 if (valuesMap == null) { 7 ConcurrentMap<Object, Supplier<V>> oldValuesMap = map.putIfAbsent(cacheKey, valuesMap = new ConcurrentHashMap<>()); 8 if (oldValuesMap != null) { 9 valuesMap = oldValuesMap; 10 } 11 } 12 Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter)); 13 Supplier<V> supplier = valuesMap.get(subKey); 14 Factory factory = null; 15 while (true) { 16 if (supplier != null) { 17 V value = supplier.get(); 18 if (value != null) { 19 return value; 20 } 21 } 22 if (factory == null) { 23 factory = new Factory(key, parameter, subKey, valuesMap); 24 } 25 if (supplier == null) { 26 supplier = valuesMap.putIfAbsent(subKey, factory); 27 if (supplier == null) { 28 supplier = factory; 29 } 30 } else { 31 if (valuesMap.replace(subKey, supplier, factory)) { 32 supplier = factory; 33 } else { 34 supplier = valuesMap.get(subKey); 35 } 36 } 37 } 38 }
咱們的調用語句是proxyClassCache.get(loader, interfaces),也就是說形參key是類加載器loader,parameter是接口的Class數組interfaces。
第2行代碼是對形參parameter(interfaces)進行非空的校驗,若是爲空則拋出空指針異常。第3行代碼是去除緩存中的陳舊數據,這裏不是咱們的關注點,就不詳細介紹了。
第4行是根據形參key(loader)計算出緩存的一級鍵cacheKey,這裏咱們不去看具體的生成邏輯,只須要大概知道一級鍵是根據形參key(loader)算出來的,這裏能夠用一個數學函數表達式描述這個關係:key=f(loader)。
第5行代碼是根據一級鍵查出值,這個值的Map實例valuesMap。因爲以前沒有爲這個loader和interfaces建立過代理類,因此valuesMap爲null,6-11行代碼會被執行,這幾行代碼就是給valueMap一個初始值,而後結合上面算出來的一級鍵cacheKey塞進緩存實例map裏面。
第12行根據key(loader)和parameter(interfaces)計算出緩存的二級鍵subKey。這裏的subKeyFactory是Proxy調用WeakCache提供的構造函數時,傳入的KeyFactory實例(上面提到過)。KeyFactory是Proxy的內部類,咱們簡單看下KeyFactory的apply方法,看下是怎麼生成二級鍵的。
1 public Object apply(ClassLoader classLoader, Class<?>[] interfaces) { 2 switch (interfaces.length) { 3 case 1: return new Key1(interfaces[0]); 4 case 2: return new Key2(interfaces[0], interfaces[1]); 5 case 0: return key0; 6 default: return new KeyX(interfaces); 7 } 8 }
這裏筆者不打算展開分析每行代碼,咱們經過上面的代碼,只須要大概知道二級鍵是根據interfaces計算出來的(classLoader這個參數根本沒用到)。這裏能夠用一個數學函數表達式描述這個關係:sub-key=g(interfaces)。
咱們繼續上面的WeakCache.get方法分析,第13行代碼是根據二級鍵subKey從valuesMap獲取值supplier,這個值supplier也就是咱們緩存數據的值。因爲valuesMap是新建的,因此supplier爲null。
15-37行是個循環,第一次進入的時候,factory和supplier都爲null。因此22-30行代碼將被執行。第23行代碼是調用Factory構造函數建立一個實例factory(Factory是WeakCache的內部類),這個構造函數就是簡單把傳入的參數賦值給factory實例的字段。接下來26-29將構造的二級鍵subKey和factory塞進valuesMap,並將factory賦給supplier(Factory類繼承Supplier類)。到這裏緩存數據的初始化就算告一段落了,一級鍵是根據loader計算出來的cacheKey,二級鍵是根據interfaces計算出來的subKey,值是new的一個factory(Supplier實例)。
第一遍的循環並無建立代理類,只是作了一些初始化的工做,下面繼續執行這個循環體(15-37行)。此次supplier不爲null了(就是上面的Factory實例factory),因此進入16-21行的代碼塊,第17行實質就是調用factory.get方法。這個方法返回的value也就是動態代理類的Class實例。緊接着第19行就把這個value返回。下面來看下Factory的get方法的源碼。
1 public synchronized V get() { 2 Supplier<V> supplier = valuesMap.get(subKey); 3 if (supplier != this) { 4 return null; 5 } 6 V value = null; 7 try { 8 value = Objects.requireNonNull(valueFactory.apply(key, parameter)); 9 } finally { 10 if (value == null) { 11 valuesMap.remove(subKey, this); 12 } 13 } 14 assert value != null; 15 CacheValue<V> cacheValue = new CacheValue<>(value); 16 if (valuesMap.replace(subKey, this, cacheValue)) { 17 reverseMap.put(cacheValue, Boolean.TRUE); 18 } else { 19 throw new AssertionError("Should not reach here"); 20 } 21 return value; 22 }
第2行代碼是根據二級鍵subKey獲得值supplier,也就是咱們在上面的WeakCache的get方法中建立的Factory實例factory。
接下來的幾行代碼沒什麼好講的,直接看第8行代碼,這行代碼調用了valueFactory.apply方法建立動態代理類並將結果賦值給變量value。
9-14行鍼對建立代理類失敗的狀況下作的處理和判斷邏輯。若是建立代理類成功,則繼續執行後面的代碼。
第15行代碼把生成的代理類的Class實例(即value變量)進行緩存。緩存的值並非直接的value,而是由value構造的一個CacheValue實例cacheValue,因爲CacheValue實現了接口Value,而Value接口繼承了Supplier接口,因此cacheValue就是Supplier的實例。這裏咱們不須要去深究CacheValue的數據結構,只須要知道緩存的值是根據代理類的Class實例去計算的,這裏能夠用一個數學函數表達式描述這個關係:value=h($ProxyX.class)。
第16行代碼將二級鍵subKey和cacheValue放入valuesMap(valuesMap的類型是ConcurrentMap<Object, Supplier<V>>)。第18行是記錄緩存狀態的。方法的最後將代理類的Class實例value返回。
這個方法的主要邏輯是對緩存的操做,動態代理類的建立動做是經過調用valueFactory.apply獲得的。這裏的valueFactory是在構造WeakCache時傳入的參數,上面提到的ProxyClassFactory實例。因爲Factory是WeakCache的內部類,因此在Factory的get方法中可使用這個實例valueFactory。下面咱們就來看下ProxyClassFactory的apply方法。
1 public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) { 2 Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length); 3 for (Class<?> intf : interfaces) { 4 Class<?> interfaceClass = null; 5 try { 6 interfaceClass = Class.forName(intf.getName(), false, loader); 7 } catch (ClassNotFoundException e) {} 8 if (interfaceClass != intf) { 9 throw new IllegalArgumentException(intf + " is not visible from class loader"); 10 } 11 if (!interfaceClass.isInterface()) { 12 throw new IllegalArgumentException(interfaceClass.getName() + " is not an interface"); 13 } 14 if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) { 15 throw new IllegalArgumentException("repeated interface: " + interfaceClass.getName()); 16 } 17 } 18 String proxyPkg = null; 19 int accessFlags = Modifier.PUBLIC | Modifier.FINAL; 20 for (Class<?> intf : interfaces) { 21 int flags = intf.getModifiers(); 22 if (!Modifier.isPublic(flags)) { 23 accessFlags = Modifier.FINAL; 24 String name = intf.getName(); 25 int n = name.lastIndexOf('.'); 26 String pkg = ((n == -1) ? "" : name.substring(0, n + 1)); 27 if (proxyPkg == null) { 28 proxyPkg = pkg; 29 } else if (!pkg.equals(proxyPkg)) { 30 throw new IllegalArgumentException("non-public interfaces from different packages"); 31 } 32 } 33 } 34 if (proxyPkg == null) { 35 proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; 36 } 37 long num = nextUniqueNumber.getAndIncrement(); 38 String proxyName = proxyPkg + proxyClassNamePrefix + num; 39 byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags); 40 try { 41 return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length); 42 } catch (ClassFormatError e) { 43 throw new IllegalArgumentException(e.toString()); 44 } 45 }
第2-17行代碼,是幾個簡單的校驗工做:1.接口interfaces是否對類加載器loader可見(即接口interfaces是由類加載器loader加載的或者由loader的上一級類加載器加載的,這點須要讀者瞭解JVM的類加載知識),2.參數interfaces是否都是接口,3.參數interfaces裏面有沒有重複數據。
第18-26行代碼,是爲即將生成的代理類計算出訪問標誌和包名。具體的約束和規則以下:
1.若是全部的接口的訪問標誌都是public,那麼生成的代理類的訪問標誌是final public,不然是final。
2.對於訪問標誌不是public的接口,它們必需要在同一個包下,不然拋出異常。
3.若是存在訪問標誌不是public的接口,那麼生成的代理類的包名就是這些接口的包名。不然包名是默認的ReflectUtil.PROXY_PACKAGE(即com.sun.proxy)。
第37-38行代碼,是計算代理類的權限定名。代理類的簡單名稱生成規則前面介紹過,是特定前綴"$Proxy"加上一個序列數(0,1,2,3...)。
第39行調用ProxyGenerator的靜態方法generateProxyClass去建立代理類的字節碼。這個方法咱們就不跟進去看了,由於它的邏輯就是根據一些固化的規則(好比代理類裏面要實現接口的方法,實現Object的equals、hashCode、toString方法,提供一個形參爲InvocationHandler實例的構造函數等等),依據JAVA虛擬機規範中定義的Class類文件結構去生成字節碼的。咱們以前在第二部分「樣例分析」中經過反編譯軟件,觀察分析過生成的代理類結構。這裏讀者能夠和前面的內容融會貫通一下。
第41行代碼,調用Proxy的本地方法defineClass0將生成的代理類字節碼加載到虛擬機,並返回代理類的Class實例。(若是讀者對類加載的只是感興趣的話,能夠去深刻學習下JAVA虛擬機的類加載機制)
到目前爲止,JDK動態代理類的建立流程就所有結束了,咱們說的是首次建立代理類的狀況,如今咱們回頭來看下WeakCache的get方法,若是以前已經爲類加載器loader和接口interfaces建立過了代理類,那麼調用這個方法的時候是個什麼樣子呢?答案就是根據一級鍵和二級鍵直接從緩存中取到代理類的Class實例。這裏就再也不逐行分析代碼了,讀者本身理解下。最後用一張簡單概要的時序圖描繪一下Proxy的getProxyClass0方法生成代理類的調用流程。
有了上面對動態代理類的建立過程的系統理解,如今來看newProxyInstance方法就容易多了,它就是使用反射機制調用動態代理類的構造函數生成一個代理類實例的過程。
1 public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException { 2 Objects.requireNonNull(h); 3 final Class<?>[] intfs = interfaces.clone(); 4 final SecurityManager sm = System.getSecurityManager(); 5 if (sm != null) { 6 checkProxyAccess(Reflection.getCallerClass(), loader, intfs); 7 } 8 Class<?> cl = getProxyClass0(loader, intfs); 9 try { 10 if (sm != null) { 11 checkNewProxyPermission(Reflection.getCallerClass(), cl); 12 } 13 final Constructor<?> cons = cl.getConstructor(constructorParams); 14 final InvocationHandler ih = h; 15 if (!Modifier.isPublic(cl.getModifiers())) { 16 AccessController.doPrivileged(new PrivilegedAction<Void>() { 17 public Void run() { 18 cons.setAccessible(true); 19 return null; 20 } 21 }); 22 } 23 return cons.newInstance(new Object[]{h}); 24 } catch (IllegalAccessException | InstantiationException e) { 25 throw new InternalError(e.toString(), e); 26 } catch (InvocationTargetException e) { 27 Throwable t = e.getCause(); 28 if (t instanceof RuntimeException) { 29 throw (RuntimeException) t; 30 } else { 31 throw new InternalError(t.toString(), t); 32 } 33 } catch (NoSuchMethodException e) { 34 throw new InternalError(e.toString(), e); 35 } 36 }
第2-7行代碼,克隆interfaces,並進行權限相關校驗,和前面getProxyClass方法相似,這裏不作贅述。
第8行getProxyClass0方法獲取代理類的Class實例,這個方法在上面也詳細介紹過了。
第10-12行也是進行權限校驗,檢查調用者是否對這個代理類有訪問權限。
第13-23行就是構造代理類實例的過程。先獲取代理類的構造函數,接着對其訪問權限進行判斷,若是不是public,則將其設置可訪問的。最後利用反射機制調用構造方法,傳入參數InvocationHandler的實例h,建立代理類實例並返回。
這個方法用於判斷一個類是否是代理類。
1 public static boolean isProxyClass(Class<?> cl) { 2 return Proxy.class.isAssignableFrom(cl) && proxyClassCache.containsValue(cl); 3 }
Proxy.class.isAssignableFrom(cl)這句話簡而言之就是判斷cl所表示的類是否是Proxy或者Proxy的子類。由於全部的代理類都集成Proxy,因此這個條件必須知足。知足這個條件也不能保證就是代理類,由於可能存在人爲地編寫一個類繼承Proxy這種狀況。proxyClassCache.containsValue(cl)這個方法是檢查緩存中是否存在這個Class實例cl。我前面分析過,但凡生成的代理類都會被緩存,因此這個方法纔是檢測一個類是不是代理類的惟一標準。
這個方法用於獲取代理類中的InvocationHandler實例。這個方法沒有什麼太多的邏輯,基本就是判斷下傳入的對象是不是代理類,以及一些訪問權限的校驗。當這些都合法的狀況下,返回InvocationHandler實例。
1 public static InvocationHandler getInvocationHandler(Object proxy) throws IllegalArgumentException { 2 if (!isProxyClass(proxy.getClass())) { 3 throw new IllegalArgumentException("not a proxy instance"); 4 } 5 final Proxy p = (Proxy) proxy; 6 final InvocationHandler ih = p.h; 7 if (System.getSecurityManager() != null) { 8 Class<?> ihClass = ih.getClass(); 9 Class<?> caller = Reflection.getCallerClass(); 10 if (ReflectUtil.needsPackageAccessCheck(caller.getClassLoader(),ihClass.getClassLoader())) { 11 ReflectUtil.checkPackageAccess(ihClass); 12 } 13 } 14 return ih; 15 }
至此,關於JDK動態代理的技術都所有講解完了。本文從基本概念、樣例分析、源碼分析三個角度去分析說明JDK動態代理背後的知識,但願對讀者有所幫助。
做者:南唐三少
出處:http://www.cnblogs.com/nantang
若是您以爲閱讀本文對您有幫助,請點一下「推薦」按鈕,您的「推薦」將是咱們最大的寫做動力!歡迎各位轉載,可是未經做者本人贊成,轉載文章以後必須在文章頁面明顯位置給出做者和原文連接,不然保留追究法律責任的權利。