動態(tài)代理類的源碼是在程序運行期間由JVM根據(jù)反射等機制動態(tài)的生成吸耿,所以不存在代理類的字節(jié)碼文件惑芭。代理類和委托類的關(guān)系是在程序運行時確定。
1淹遵、與動態(tài)代理緊密關(guān)聯(lián)的Java API口猜。
1)java.lang.reflect.Proxy
2)java.lang.reflect.InvocationHandler
3)java.lang.ClassLoader
2、動態(tài)代理的實現(xiàn)步驟
1)創(chuàng)建接口
public interface IDrink {
void drink();
}
2)創(chuàng)建被代理類
public class Coffee implements IDrink {
public void drink(){
System.out.println("喝咖啡");
}
}
3)創(chuàng)建代理工具類
public class MilkHandler implements InvocationHandler {
Object obj = null;
public MilkHandler(Object obj){
this.obj = obj;
}
//proxy:被代理對象 method:需要被增強的方法 args:方法的參數(shù)
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("加牛奶");
method.invoke(obj, args);
return null;
}
}
4)測試
Coffee c = new Coffee();
MilkHandler h = new MilkHandler(c);
IDrink d= (IDrink) Proxy.newProxyInstance(c.getClass().getClassLoader(), c.getClass().getInterfaces(), h);
d.drink();
動態(tài)代理機制的特點
首先是動態(tài)生成的代理類本身的一些特點透揣。
包:如果所代理的接口都是 public 的济炎,那么它將被定義在頂層包(即包路徑為空),如果所代理的接口中有非 public 的接口(因為接口不能被定義為 protect 或 private辐真,所以除 public 之外就是默認的 package 訪問級別)冻辩,那么它將被定義在該接口所在包(假設(shè)代理了 com.ibm.developerworks 包中的某非 public 接口 A,那么新生成的代理類所在的包就是 com.ibm.developerworks)拆祈,這樣設(shè)計的目的是為了最大程度的保證動態(tài)代理類不會因為包管理的問題而無法被成功定義并訪問
類修飾符:該代理類具有 final 和 public 修飾符恨闪,意味著它可以被所有的類訪問,但是不能被再度繼承放坏;
類名:格式是“$ProxyN”咙咽,其中 N 是一個逐一遞增的阿拉伯?dāng)?shù)字,代表 Proxy 類第 N 次生成的動態(tài)代理類淤年,值得注意的一點是钧敞,并不是每次調(diào)用 Proxy 的靜態(tài)方法創(chuàng)建動態(tài)代理類都會使得 N 值增加,原因是如果對同一組接口(包括接口排列的順序相同)試圖重復(fù)創(chuàng)建動態(tài)代理類麸粮,它會很聰明地返回先前已經(jīng)創(chuàng)建好的代理類的類對象溉苛,而不會再嘗試去創(chuàng)建一個全新的代理類,這樣可以節(jié)省不必要的代碼重復(fù)生成弄诲,提高了代理類的創(chuàng)建效率愚战。
動態(tài)代理的優(yōu)缺點
優(yōu)點
動態(tài)代理與靜態(tài)代理相比較,最大的好處是接口中聲明的所有方法都被轉(zhuǎn)移到調(diào)用處理器一個集中的方法中處理(InvocationHandler.invoke)齐遵。這樣寂玲,在接口方法數(shù)量比較多的時候,我們可以進行靈活處理梗摇,而不需要像靜態(tài)代理那樣每一個方法進行中轉(zhuǎn)拓哟。在本示例中看不出來,因為invoke方法體內(nèi)嵌入了具體的外圍業(yè)務(wù)(記錄任務(wù)處理前后時間并計算時間差)伶授,實際中可以類似Spring AOP那樣配置外圍業(yè)務(wù)断序。
缺點
誠然流纹,Proxy 已經(jīng)設(shè)計得非常優(yōu)美,但是還是有一點點小小的遺憾之處违诗,那就是它始終無法擺脫僅支持 interface 代理的桎梏捧颅,因為它的設(shè)計注定了這個遺憾〗系瘢回想一下那些動態(tài)生成的代理類的繼承關(guān)系圖碉哑,它們已經(jīng)注定有一個共同的父類叫 Proxy。Java 的繼承機制注定了這些動態(tài)代理類們無法實現(xiàn)對 class 的動態(tài)代理亮蒋,原因是多繼承在 Java 中本質(zhì)上就行不通扣典。
有很多條理由,人們可以否定對 class 代理的必要性慎玖,但是同樣有一些理由贮尖,相信支持 class 動態(tài)代理會更美好。接口和類的劃分趁怔,本就不是很明顯湿硝,只是到了 Java 中才變得如此的細化。如果只從方法的聲明及是否被定義來考量润努,有一種兩者的混合體关斜,它的名字叫抽象類。實現(xiàn)對抽象類的動態(tài)代理铺浇,相信也有其內(nèi)在的價值痢畜。此外,還有一些歷史遺留的類鳍侣,它們將因為沒有實現(xiàn)任何接口而從此與動態(tài)代理永世無緣丁稀。如此種種,不得不說是一個小小的遺憾倚聚。