關(guān)于動(dòng)態(tài)代理和靜態(tài)代理
當(dāng)一個(gè)對象(客戶端)不能或者不想直接引用另一個(gè)對象(目標(biāo)對象)呀邢,這時(shí)可以應(yīng)用代理模式在這兩者之間構(gòu)建一個(gè)橋梁--代理對象鼠次。
按照代理對象的創(chuàng)建時(shí)期不同滤愕,可以分為兩種:
靜態(tài)代理:事先寫好代理對象類,在程序發(fā)布前就已經(jīng)存在了谎僻;
動(dòng)態(tài)代理:應(yīng)用程序發(fā)布后撤逢,通過動(dòng)態(tài)創(chuàng)建代理對象膛锭。
靜態(tài)代理其實(shí)就是一個(gè)典型的代理模式實(shí)現(xiàn),在代理類中包裝一個(gè)被代理對象蚊荣,然后影響被代理對象的行為初狰,比較簡單,代碼就不放了互例。
其中動(dòng)態(tài)代理又可分為:JDK動(dòng)態(tài)代理和CGLIB代理奢入。
1.JDK動(dòng)態(tài)代理
此時(shí)代理對象和目標(biāo)對象實(shí)現(xiàn)了相同的接口,目標(biāo)對象作為代理對象的一個(gè)屬性媳叨,具體接口實(shí)現(xiàn)中腥光,可以在調(diào)用目標(biāo)對象相應(yīng)方法前后加上其他業(yè)務(wù)處理邏輯关顷。
代理模式在實(shí)際使用時(shí)需要指定具體的目標(biāo)對象,如果為每個(gè)類都添加一個(gè)代理類的話武福,會(huì)導(dǎo)致類很多议双,同時(shí)如果不知道具體類的話,怎樣實(shí)現(xiàn)代理模式呢捉片?這就引出動(dòng)態(tài)代理平痰。
JDK動(dòng)態(tài)代理只能針對實(shí)現(xiàn)了接口的類生成代理。
具體實(shí)現(xiàn)原理:
1伍纫、通過實(shí)現(xiàn)InvocationHandlet接口創(chuàng)建自己的調(diào)用處理器
2宗雇、通過為Proxy類指定ClassLoader對象和一組interface來創(chuàng)建動(dòng)態(tài)代理
3、通過反射機(jī)制獲取動(dòng)態(tài)代理類的構(gòu)造函數(shù)莹规,其唯一參數(shù)類型就是調(diào)用處理器接口類型
4赔蒲、通過構(gòu)造函數(shù)創(chuàng)建動(dòng)態(tài)代理類實(shí)例,構(gòu)造時(shí)調(diào)用處理器對象作為參數(shù)參入
JDK動(dòng)態(tài)代理是面向接口的代理模式访惜,如果被代理目標(biāo)沒有接口那么Spring也無能為力嘹履,
Spring通過java的反射機(jī)制生產(chǎn)被代理接口的新的匿名實(shí)現(xiàn)類,重寫了其中AOP的增強(qiáng)方法债热。
2.CGLIB代理
CGLIB(CODE GENERLIZE LIBRARY)代理是針對類實(shí)現(xiàn)代理砾嫉,
主要是對指定的類生成一個(gè)子類,覆蓋其中的所有方法窒篱,所以該類或方法不能聲明稱final的焕刮。
CGLib是一個(gè)強(qiáng)大、高性能的Code生產(chǎn)類庫墙杯,可以實(shí)現(xiàn)運(yùn)行期動(dòng)態(tài)擴(kuò)展java類配并,Spring在運(yùn)行期間通過
CGlib繼承要被動(dòng)態(tài)代理的類,重寫父類的方法高镐,實(shí)現(xiàn)AOP面向切面編程溉旋。
JDK動(dòng)態(tài)代理和CGLIB代理生成的區(qū)別
JDK動(dòng)態(tài)代理只能對實(shí)現(xiàn)了接口的類生成代理,而不能針對類 嫉髓。
CGLIB是針對類實(shí)現(xiàn)代理观腊,主要是對指定的類生成一個(gè)子類,覆蓋其中的方法 算行。
因?yàn)槭抢^承梧油,所以該類或方法最好不要聲明成final ,final可以阻止繼承和多態(tài)州邢。
兩者速度對比
JDK動(dòng)態(tài)代理是面向接口儡陨,在創(chuàng)建代理實(shí)現(xiàn)類時(shí)比CGLib要快,創(chuàng)建代理速度快。
CGLib動(dòng)態(tài)代理是通過字節(jié)碼底層繼承要代理類來實(shí)現(xiàn)(如果被代理類被final關(guān)鍵字所修飾骗村,那么抱歉會(huì)失斚油省),在創(chuàng)建代理這一塊沒有JDK動(dòng)態(tài)代理快胚股,但是運(yùn)行速度比JDK動(dòng)態(tài)代理要快渔扎。
PS:
- final 所修飾的數(shù)據(jù)具有“終態(tài)”的特征,表示“最終的”意思:
- final 修飾的類不能被繼承信轿。
- final 修飾的方法不能被子類重寫。
- final 修飾的變量(成員變量或局部變量)即成為常量残吩,只能賦值一次财忽。
- final 修飾的成員變量必須在聲明的同時(shí)賦值,如果在聲明的時(shí)候沒有賦值泣侮,那么只有 一次賦值的機(jī)會(huì)即彪,而且只能在構(gòu)造方法中顯式賦值,然后才能使用活尊。
- final 修飾的局部變量可以只聲明不賦值隶校,然后再進(jìn)行一次性的賦值。
參考代碼
CGLIB:
public Object createProxyObject(Object obj) {
this.targetObject = obj;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(obj.getClass());
enhancer.setCallback(this);
Object proxyObj = enhancer.create();
return proxyObj;// 返回代理對象蛹锰,返回的對象其實(shí)就是一個(gè)封裝了“實(shí)現(xiàn)類”的代理類深胳,是實(shí)現(xiàn)類的實(shí)例。
}
JDK:
public Object newProxy(Object targetObject) {// 將目標(biāo)對象傳入進(jìn)行代理
this.targetObject = targetObject; //注意這個(gè)方法的參數(shù)铜犬,后面是類實(shí)現(xiàn)的接口
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(), this);// 返回代理對象
}
在代碼中可以看到舞终,在生成代理類時(shí),傳遞的是實(shí)現(xiàn)類所實(shí)現(xiàn)的接口 targetObject.getClass().getInterfaces()癣猾,所以JDK只能對于接口進(jìn)行做代理敛劝。如果換成類的話,則會(huì)拋java.lang.ClassCastException異常纷宇。
在Spring的源碼中夸盟,可以看到很多生成代理類的代碼。
動(dòng)態(tài)代理的應(yīng)用
AOP(Aspect-OrientedProgramming像捶,面向切面編程)上陕,AOP包括切面(aspect)、通知(advice)作岖、連接點(diǎn)(joinpoint)唆垃,實(shí)現(xiàn)方式就是通過對目標(biāo)對象的代理在連接點(diǎn)前后加入通知,完成統(tǒng)一的切面操作痘儡。
實(shí)現(xiàn)AOP的技術(shù)辕万,主要分為兩大類:
一是采用動(dòng)態(tài)代理技術(shù),利用截取消息的方式,對該消息進(jìn)行裝飾渐尿,以取代原有對象行為的執(zhí)行醉途;
二是采用靜態(tài)織入的方式,引入特定的語法創(chuàng)建“方面”砖茸,從而使得編譯器可以在編譯期間織入有關(guān)“方面”的代碼隘擎。
Spring提供了兩種方式來生成代理對象: JDKProxy和Cglib,具體使用哪種方式生成由AopProxyFactory根據(jù)AdvisedSupport對象的配置來決定凉夯。
默認(rèn)的策略是如果目標(biāo)類是接口货葬,則使用JDK動(dòng)態(tài)代理技術(shù),如果目標(biāo)對象沒有實(shí)現(xiàn)接口劲够,則默認(rèn)會(huì)采用CGLIB代理震桶。
如果目標(biāo)對象實(shí)現(xiàn)了接口,可以強(qiáng)制使用CGLIB實(shí)現(xiàn)代理(添加CGLIB庫征绎,并在spring配置中加入<aop:config proxy-target-class="true"/>)蹲姐。
Spring動(dòng)態(tài)代理機(jī)制:
Spirng的AOP的動(dòng)態(tài)代理實(shí)現(xiàn)機(jī)制也是這兩種:JDK動(dòng)態(tài)代理和CGLib動(dòng)態(tài)代理
一般而言Spring默認(rèn)優(yōu)先使用JDK動(dòng)態(tài)代理技術(shù),只有在被代理類沒有實(shí)現(xiàn)接口時(shí)人柿,才會(huì)選擇使用CGLIB技術(shù)來實(shí)現(xiàn)AOP柴墩。
但是也提供了配置參數(shù)來強(qiáng)制選擇使用 CGLIB 技術(shù),如下:
<aop:config proxy-target-class="true" />
proxy-target-class="true" 表示強(qiáng)制使用 CGLIB 技術(shù)來實(shí)現(xiàn)AOP凫岖,因?yàn)镃GLIB是生成子類也就是代理類來實(shí)現(xiàn)的江咳,所以proxy-target-class,表示是否代理目標(biāo)類哥放。
如果寫成<aop:config />扎阶,proxy-target-class配置缺省, 就會(huì)由spring來選擇婶芭,spring優(yōu)先使用JDK動(dòng)態(tài)代理來實(shí)現(xiàn)AOP,只有在被代理類沒有實(shí)現(xiàn)接口時(shí)东臀,才會(huì)選擇使用CGLIB技術(shù)來實(shí)現(xiàn)AOP
參考文章
http://www.cnblogs.com/binyue/p/4519652.html
http://blog.csdn.net/qq1723205668/article/details/56481476