之前介紹的反射和注解都是Java中的動(dòng)態(tài)特性,還有即將介紹的動(dòng)態(tài)代理也是Java中的一個(gè)動(dòng)態(tài)特性。這些動(dòng)態(tài)特性使得我們的程序很靈活。動(dòng)態(tài)代理是面向AOP編程的基礎(chǔ)。通過(guò)動(dòng)態(tài)代理,我們可以在運(yùn)行時(shí)動(dòng)態(tài)創(chuàng)建一個(gè)類,實(shí)現(xiàn)某些接口中的方法,目前為止該特性已被廣泛應(yīng)用于各種框架和類庫(kù)中,例如:Spring,Hibernate,MyBatis等。理解動(dòng)態(tài)代理是理解框架底層的基礎(chǔ)。
主要內(nèi)容如下:
理解代理是何意
Java SDK實(shí)現(xiàn)動(dòng)態(tài)代理
第三方庫(kù)cglib實(shí)現(xiàn)動(dòng)態(tài)代理
一、代理的概念
單從字面上理解,代理就是指原對(duì)象的委托人,它不是原對(duì)象但是卻有原對(duì)象的權(quán)限。Java中的代理意思類似,就是指通過(guò)代理來(lái)操作原對(duì)象的方法和屬性,而原對(duì)象不直接出現(xiàn)。這樣做有幾點(diǎn)好處:
節(jié)省創(chuàng)建原對(duì)象的高開(kāi)銷,創(chuàng)建一個(gè)代理并不會(huì)立馬創(chuàng)建一個(gè)實(shí)際原對(duì)象,而是保存一個(gè)原對(duì)象的地址,按需加載
執(zhí)行權(quán)限檢查,保護(hù)原對(duì)象
實(shí)際上代理堵在了原對(duì)象的前面,在代理的內(nèi)部往往還是調(diào)用了原對(duì)象的方法,只是它還做了其他的一些操作。下面看第一種實(shí)現(xiàn)動(dòng)態(tài)代理的方式。
二、Java SDK實(shí)現(xiàn)動(dòng)態(tài)代理
實(shí)現(xiàn)動(dòng)態(tài)代理主要有如下幾個(gè)步驟:
實(shí)現(xiàn) InvocationHandler接口,完成自定義調(diào)用處理器
通過(guò)Proxy的getProxyClass方法獲取對(duì)應(yīng)的代理類
利用反射技術(shù)獲取該代理類的constructor構(gòu)造器
利用constructor構(gòu)造代理實(shí)例對(duì)象
在一步步解析源碼之前,我們先通過(guò)一個(gè)完整的實(shí)例了解下,整個(gè)程序的一步步邏輯走向。
//定義了一個(gè)調(diào)用處理器public class MyInvotion implements InvocationHandler { private Object realObj; public MyInvotion(Object obj){ this.realObj = obj; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{ //通過(guò)代理執(zhí)行原對(duì)象的方法 return method.invoke(realObj,args); } }
//定義一個(gè)接口public interface MyInterface { public void sayHello(); }//該接口的一個(gè)實(shí)現(xiàn)類,該類就是我們的原對(duì)象public class ClassA implements MyInterface { public void sayHello(){ System.out.println("hello walker"); } }
//main 函數(shù)public static void main(String[] args){ ClassA a = new ClassA(); MyInvotion myInvotion = new MyInvotion(a); Class myProxy = Proxy.getProxyClass(ClassA.class.getClassLoader(), new Class[]{MyInterface.class}); Constructor constructor =myProxy.getConstructor(new Class[]{InvocationHandler.class}); MyInterface m = (MyInterface)constructor.newInstance(myInvotion); m.sayHello(); }
輸出結(jié)果:hello walker
簡(jiǎn)單說(shuō)下整體的運(yùn)行過(guò)程,首先我們創(chuàng)建ClassA 實(shí)例并將它傳入自定義的調(diào)用處理器MyInvotion,在MyInvotion中用realObj接受該參數(shù)代表原對(duì)象。接著調(diào)用Proxy的getProxyClass方法,將ClassA 的類加載器和ClassA 的實(shí)現(xiàn)的接口集合傳入,該方法內(nèi)部會(huì)實(shí)現(xiàn)所有接口返回該類的代理類,然后我們利用反射獲取代理類的構(gòu)造器并創(chuàng)建實(shí)例。
以上便是整個(gè)程序運(yùn)行的大致流程,接下來(lái)我們從源代碼的角度看看具體是如何實(shí)現(xiàn)的。首先我們看InvocationHandler接口,這是我們的調(diào)用處理器,在代理類中訪問(wèn)的所有的方法都會(huì)被轉(zhuǎn)發(fā)到這執(zhí)行,具體的等我們看了代理類源碼及理解了。該接口中唯一的方法是:
public Object invoke(Object proxy, Method method, Object[] args)
參數(shù)Proxy表示動(dòng)態(tài)生成的代理類的對(duì)象,基本沒(méi)啥用
參數(shù)method表示當(dāng)前正在被調(diào)用的方法
數(shù)組args指定了該方法的參數(shù)集合
我們上例中對(duì)該接口的實(shí)現(xiàn)情況,定義了一個(gè)realObj用于保存原對(duì)象的引用。重寫(xiě)的invoke方法中調(diào)用了原對(duì)象realObj的method方法,具體誰(shuí)來(lái)調(diào)用該方法以及傳入的參數(shù)是什么,在看完代理類源碼即可知曉。
接下來(lái)我們看看最核心的內(nèi)容,如何動(dòng)態(tài)創(chuàng)建代理類。這是getProxyClass方法的源碼:
public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) throws IllegalArgumentException { final Class<?>[] intfs = interfaces.clone(); final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } return getProxyClass0(loader, intfs); }
首先獲取了該類實(shí)現(xiàn)的所有的接口的集合,然后判斷創(chuàng)建該代理是否具有安全性問(wèn)題,檢查接口類對(duì)象是否對(duì)類裝載器可見(jiàn)等。然后調(diào)用另外一個(gè)getProxyClass0方法,我們跟進(jìn)去:
private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) { if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } // If the proxy class defined by the given loader implementing // the given interfaces exists, this will simply return the cached copy; // otherwise, it will create the proxy class via the ProxyClassFactory return proxyClassCache.get(loader, interfaces); }
判斷如果該類的接口超過(guò)65535(想必還沒(méi)有那么牛的類),拋出異常。在我們的Proxy類中有個(gè)屬性proxyClassCache,這是一個(gè)WeakCache類型的靜態(tài)變量。它指示了我們的類加載器和代理類之間的映射。所以proxyClassCache的get方法用于根據(jù)類加載器來(lái)獲取Proxy類,如果已經(jīng)存在則直接從cache中返回,如果沒(méi)有則創(chuàng)建一個(gè)映射并更新cache表。具體創(chuàng)建一個(gè)Proxy類并存入cache表中的代碼限于能力,未能參透。
至此我們就獲取到了該ClassA類對(duì)應(yīng)的代理類型,接著我們通過(guò)該類的getConstructor方法獲取該代理類的構(gòu)造器,并傳入InvocationHandler.class作為參數(shù),至于為何要傳入該類型作為參數(shù),等會(huì)看代理類源碼變一目了然了。
最后newInstance創(chuàng)建該代理類的實(shí)例,實(shí)現(xiàn)對(duì)ClassA對(duì)象的代理。
可能看完上述的介紹,你還會(huì)有點(diǎn)暈。下面我們通過(guò)查看動(dòng)態(tài)生成的代理類的源碼來(lái)加深理解。上述getProxyClass方法會(huì)動(dòng)態(tài)創(chuàng)建一個(gè)代理類并返回他的Class類型,這個(gè)代理類一般被命名為$ProxyN,這個(gè)N是遞增的用于標(biāo)記不同的代理類。我們可以利用反編譯工具反編譯該class:
final class $Proxy0 extends Proxy implements MyInterface { private static Method m1; private static Method m3; private static Method m2; private static Method m0; public $Proxy0(InvocationHandler paramInvocationHandler) { super(paramInvocationHandler); } public final boolean equals(Object paramObject) { return ((Boolean) this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue(); } public final void sayHello() { this.h.invoke(this, m3, null); } public final String toString() { return (String) this.h.invoke(this, m2, null); } public final int hashCode() { return ((Integer) this.h.invoke(this, m0, null)).intValue(); } static { m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m3 = Class.forName("laoma.demo.proxy.SimpleJDKDynamicProxyDemo$IService") .getMethod("sayHello",new Class[0]); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); } }
這就是上述的ClassA的動(dòng)態(tài)代理類,我們看到該類的構(gòu)造方法傳入?yún)?shù)InvocationHandler類型,并調(diào)用了父類Proxy的構(gòu)造方法保存了這個(gè)InvocationHandler實(shí)例,這也解釋了我們?yōu)槭裁丛讷@取構(gòu)造器的時(shí)候需要指定參數(shù)類型為InvocationHandler,就是因?yàn)閯?dòng)態(tài)代理類只有一個(gè)構(gòu)造器并且參數(shù)類型為InvocationHandler。
接著我們看其中的方法,貌似只有一個(gè)sayHello是我們知道的,別的方法哪來(lái)的?我們說(shuō)過(guò)在動(dòng)態(tài)創(chuàng)建代理類的時(shí)候,會(huì)實(shí)現(xiàn)原對(duì)象的所有接口。所以sayHello方法是實(shí)現(xiàn)的MyInterface。而其余的四個(gè)方法是代理類由于比較常用,被默認(rèn)添加到其中。而這些方法的內(nèi)部都是調(diào)用的this.h.invoke這個(gè)方法,this.h就是保存在父類Proxy中的InvocationHandler實(shí)例(我們用構(gòu)造器向其中保存的),調(diào)用了這個(gè)類的invoke方法,在我們自定義的InvocationHandler實(shí)例中重寫(xiě)了invoke方法,我們寫(xiě)的比較簡(jiǎn)單,直接執(zhí)行傳入的method。
也就是我們調(diào)用代理類的任何一個(gè)方法都會(huì)轉(zhuǎn)發(fā)到該InvocationHandler實(shí)例中的involve中,因?yàn)樵搶?shí)例中保存有我們的原對(duì)象,所以我們可以選擇直接調(diào)取原對(duì)象中的方法作為回調(diào)。
以上便是有關(guān)Java SDK中動(dòng)態(tài)代理的相關(guān)內(nèi)容,稍微總結(jié)下,首先我們通過(guò)實(shí)現(xiàn)InvocationHandler自定義一個(gè)調(diào)用處理類,該類中會(huì)保存我們的原對(duì)象,并提供一個(gè)invoke方法供代理類使用。然后我們通過(guò)getProxyClass方法動(dòng)態(tài)創(chuàng)建代理類,最后用反射獲取代理類的實(shí)例對(duì)象。
需要注意的是:以上我們使用的四步創(chuàng)建代理實(shí)例時(shí)最根本的,其實(shí)Proxy中提供一個(gè)方法可以封裝2到4步的操作。上述代碼也可以這么寫(xiě):
ClassA a = new ClassA(); MyInterface aProxy = (MyInterface)Proxy.newProxyInstance(ClassA.class.getClassLoader(),new Class<?>[]{MyInterface.class},new MyInvotion(a)); aProxy.sayHello();
我們打開(kāi)該方法的內(nèi)部源碼,其實(shí)走的還是我們上述的過(guò)程,它就是做了封裝。
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h){ Objects.requireNonNull(h); //獲取所有接口 final Class<?>[] intfs = interfaces.clone(); final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } //創(chuàng)建動(dòng)態(tài)代理類 Class<?> cl = getProxyClass0(loader, intfs); try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } 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; } }); } return cons.newInstance(new Object[]{h}); ............. .............. }
二、第三方庫(kù)cglib實(shí)現(xiàn)動(dòng)態(tài)代理
使用動(dòng)態(tài)代理,我們編寫(xiě)通用的代碼邏輯,即僅實(shí)現(xiàn)一個(gè)InvocationHandler實(shí)例完成對(duì)多個(gè)類型的代理。但是我們從動(dòng)態(tài)生成的代理類的源碼可以看到,所有的代理類都繼承自Proxy這個(gè)類,這就導(dǎo)致我們這種方式不能代理類,只能代理接口。因?yàn)閖ava中是單繼承的。也就是說(shuō),給我們一個(gè)類型,我們只能動(dòng)態(tài)實(shí)現(xiàn)該類所有的接口類型,但是該類繼承的別的類我們?cè)诖眍愔惺遣荒苁褂玫?,因?yàn)樗鼪](méi)有被代理類繼承。下面看個(gè)例子:
public class ClassB { public void welcome(){ System.out.println("welcom walker"); } }public interface MyInterface { public void sayHello(); }//需要被代理的原類型,繼承了ClassB和接口MyInterface public class ClassA extends ClassB implements MyInterface { public void sayHello(){ System.out.println("hello walker"); } }
//InvocationHandler 實(shí)例public class MyInvotion implements InvocationHandler {private Object realObj;public MyInvotion(Object obj){ this.realObj = obj; }public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{ return method.invoke(realObj,args); } }
我們反編譯該代理類,和上述的源碼是一樣的,此處不再重復(fù)貼出。我們能從中看出來(lái)的是,我們的代理只會(huì)實(shí)現(xiàn)原類型中所有的接口,至于原類型所繼承的類,在生成Proxy代理類的時(shí)候會(huì)丟棄,因?yàn)樗械拇眍惐仨毨^承Proxy類,這就導(dǎo)致原類型的父類中的方法 在代理類中丟失。這是該種方式的一大弊端。下面我們看看另一種方式實(shí)現(xiàn)動(dòng)態(tài)代理,該種方式完美解決了這種不足。
限于篇幅,我們下篇介紹cglib實(shí)現(xiàn)動(dòng)態(tài)代理機(jī)制的內(nèi)容,本篇暫時(shí)結(jié)束,總結(jié)的不好,望海涵。
http://www.cnblogs.com/yangming1996/p/6820180.html