2020-01-07

Java的三種代理模式

1.代理模式

代理(Proxy)是一種設計模式,提供了對目標對象另外的訪問方式;即通過代理對象訪問目標對象.這樣做的好處是:可以在目標對象實現(xiàn)的基礎上,增強額外的功能操作,即擴展目標對象的功能.

這里使用到編程中的一個思想:不要隨意去修改別人已經(jīng)寫好的代碼或者方法,如果需改修改,可以通過代理的方式來擴展該方法

舉個例子來說明代理的作用:假設我們想邀請一位明星,那么并不是直接連接明星,而是聯(lián)系明星的經(jīng)紀人,來達到同樣的目的.明星就是一個目標對象,他只要負責活動中的節(jié)目,而其他瑣碎的事情就交給他的代理人(經(jīng)紀人)來解決.這就是代理思想在現(xiàn)實中的一個例子

用圖表示如下:

代理模式的關鍵點是:代理對象與目標對象.代理對象是對目標對象的擴展,并會調(diào)用目標對象

1.1.靜態(tài)代理

靜態(tài)代理在使用時,需要定義接口或者父類,被代理對象與代理對象一起實現(xiàn)相同的接口或者是繼承相同父類.

下面舉個案例來解釋:

模擬保存動作,定義一個保存動作的接口:IUserDao.java,然后目標對象實現(xiàn)這個接口的方法UserDao.java,此時如果使用靜態(tài)代理方式,就需要在代理對象(UserDaoProxy.java)中也實現(xiàn)IUserDao接口.調(diào)用的時候通過調(diào)用代理對象的方法來調(diào)用目標對象.

需要注意的是,代理對象與目標對象要實現(xiàn)相同的接口,然后通過調(diào)用相同的方法來調(diào)用目標對象的方法

用圖表示如下:


靜態(tài)代理

代碼示例:

接口:IUserDao.java

/**

* 接口

*/publicinterfaceIUserDao{voidsave();}

目標對象:UserDao.java

/**

* 接口實現(xiàn)

* 目標對象

*/publicclassUserDaoimplementsIUserDao{publicvoidsave(){? ? ? ? System.out.println("----已經(jīng)保存數(shù)據(jù)!----");? ? }}

代理對象:UserDaoProxy.java

/**

* 代理對象,靜態(tài)代理

*/publicclassUserDaoProxyimplementsIUserDao{//接收保存目標對象privateIUserDao target;publicUserDaoProxy(IUserDao target){this.target=target;? ? }publicvoidsave(){? ? ? ? System.out.println("開始事務...");? ? ? ? target.save();//執(zhí)行目標對象的方法System.out.println("提交事務...");? ? }}

測試類:App.java

/**

* 測試類

*/publicclassApp{publicstaticvoidmain(String[] args){//目標對象UserDao target =newUserDao();//代理對象,把目標對象傳給代理對象,建立代理關系UserDaoProxy proxy =newUserDaoProxy(target);? ? ? ? proxy.save();//執(zhí)行的是代理的方法}}

靜態(tài)代理總結(jié):

1.可以做到在不修改目標對象的功能前提下,對目標功能擴展.

2.缺點:

因為代理對象需要與目標對象實現(xiàn)一樣的接口,所以會有很多代理類,類太多.同時,一旦接口增加方法,目標對象與代理對象都要維護.

如何解決靜態(tài)代理中的缺點呢?答案是可以使用動態(tài)代理方式

1.2.動態(tài)代理(jdk動態(tài)代理)

動態(tài)代理有以下特點:

1.代理對象,不需要實現(xiàn)接口

2.代理對象的生成,是利用JDK的API,動態(tài)的在內(nèi)存中構(gòu)建代理對象(需要我們指定創(chuàng)建代理對象/目標對象實現(xiàn)的接口的類型)

3.動態(tài)代理也叫做:JDK代理,接口代理

JDK中生成代理對象的API

代理類所在包:java.lang.reflect.Proxy

JDK實現(xiàn)代理只需要使用newProxyInstance方法,但是該方法需要接收三個參數(shù),完整的寫法是:

staticObjectnewProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )

注意該方法是在Proxy類中是靜態(tài)方法,且接收的三個參數(shù)依次為:

ClassLoader loader,:指定當前目標對象使用類加載器,獲取加載器的方法是固定的

Class<?>[] interfaces,:目標對象實現(xiàn)的接口的類型,使用泛型方式確認類型

InvocationHandler h:事件處理,執(zhí)行目標對象的方法時,會觸發(fā)事件處理器的方法,會把當前執(zhí)行目標對象的方法作為參數(shù)傳入

代碼示例:

接口類IUserDao.java以及接口實現(xiàn)類,目標對象UserDao是一樣的,沒有做修改.在這個基礎上,增加一個代理工廠類(ProxyFactory.java),將代理類寫在這個地方,然后在測試類(需要使用到代理的代碼)中先建立目標對象和代理對象的聯(lián)系,然后代用代理對象的中同名方法

代理工廠類:ProxyFactory.java

/**

* 創(chuàng)建動態(tài)代理對象

* 動態(tài)代理不需要實現(xiàn)接口,但是需要指定接口類型

*/publicclassProxyFactory{//維護一個目標對象privateObject target;publicProxyFactory(Object target){this.target=target;? ? }//給目標對象生成代理對象publicObjectgetProxyInstance(){returnProxy.newProxyInstance(? ? ? ? ? ? ? ? target.getClass().getClassLoader(),? ? ? ? ? ? ? ? target.getClass().getInterfaces(),newInvocationHandler() {@OverridepublicObjectinvoke(Object proxy, Method method, Object[] args)throwsThrowable{? ? ? ? ? ? ? ? ? ? ? ? System.out.println("開始事務2");//執(zhí)行目標對象方法Object returnValue = method.invoke(target, args);? ? ? ? ? ? ? ? ? ? ? ? System.out.println("提交事務2");returnreturnValue;? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? }? ? ? ? );? ? }}

測試類:App.java

/**

* 測試類

*/publicclassApp{publicstaticvoidmain(String[] args){// 目標對象IUserDao target =newUserDao();// 【原始的類型 class cn.itcast.b_dynamic.UserDao】System.out.println(target.getClass());// 給目標對象夷恍,創(chuàng)建代理對象IUserDao proxy = (IUserDao)newProxyFactory(target).getProxyInstance();// class $Proxy0? 內(nèi)存中動態(tài)生成的代理對象System.out.println(proxy.getClass());// 執(zhí)行方法? 【代理對象】proxy.save();? ? }}

總結(jié):

代理對象不需要實現(xiàn)接口,但是目標對象一定要實現(xiàn)接口,否則不能用動態(tài)代理

1.3.Cglib代理

上面的靜態(tài)代理和動態(tài)代理模式都是要求目標對象是實現(xiàn)一個接口的目標對象,但是有時候目標對象只是一個單獨的對象,并沒有實現(xiàn)任何的接口,這個時候就可以使用以目標對象子類的方式類實現(xiàn)代理,這種方法就叫做:Cglib代理

Cglib代理,也叫作子類代理,它是在內(nèi)存中構(gòu)建一個子類對象從而實現(xiàn)對目標對象功能的擴展.

JDK的動態(tài)代理有一個限制,就是使用動態(tài)代理的對象必須實現(xiàn)一個或多個接口,如果想代理沒有實現(xiàn)接口的類,就可以使用Cglib實現(xiàn).

Cglib是一個強大的高性能的代碼生成包,它可以在運行期擴展java類與實現(xiàn)java接口.它廣泛的被許多AOP的框架使用,例如Spring AOP和synaop,為他們提供方法的interception(攔截)

Cglib包的底層是通過使用一個小而塊的字節(jié)碼處理框架ASM來轉(zhuǎn)換字節(jié)碼并生成新的類.不鼓勵直接使用ASM,因為它要求你必須對JVM內(nèi)部結(jié)構(gòu)包括class文件的格式和指令集都很熟悉.

Cglib子類代理實現(xiàn)方法:

1.需要引入cglib的jar文件,但是Spring的核心包中已經(jīng)包括了Cglib功能,所以直接引入pring-core-3.2.5.jar即可.

2.引入功能包后,就可以在內(nèi)存中動態(tài)構(gòu)建子類

3.代理的類不能為final,否則報錯

4.目標對象的方法如果為final/static,那么就不會被攔截,即不會執(zhí)行目標對象額外的業(yè)務方法.

代碼示例:

目標對象類:UserDao.java

/**

* 目標對象,沒有實現(xiàn)任何接口

*/publicclassUserDao{publicvoidsave(){? ? ? ? System.out.println("----已經(jīng)保存數(shù)據(jù)!----");? ? }}

Cglib代理工廠:ProxyFactory.java

/**

* Cglib子類代理工廠

* 對UserDao在內(nèi)存中動態(tài)構(gòu)建一個子類對象

*/publicclassProxyFactoryimplementsMethodInterceptor{//維護目標對象privateObject target;publicProxyFactory(Object target){this.target = target;? ? }//給目標對象創(chuàng)建一個代理對象publicObjectgetProxyInstance(){//1.工具類Enhancer en =newEnhancer();//2.設置父類en.setSuperclass(target.getClass());//3.設置回調(diào)函數(shù)en.setCallback(this);//4.創(chuàng)建子類(代理對象)returnen.create();? ? }@OverridepublicObjectintercept(Object obj, Method method, Object[] args, MethodProxy proxy)throwsThrowable{? ? ? ? System.out.println("開始事務...");//執(zhí)行目標對象的方法Object returnValue = method.invoke(target, args);? ? ? ? System.out.println("提交事務...");returnreturnValue;? ? }}

測試類:

/**

* 測試類

*/publicclassApp{@Testpublicvoidtest(){//目標對象UserDao target =newUserDao();//代理對象UserDao proxy = (UserDao)newProxyFactory(target).getProxyInstance();//執(zhí)行代理對象的方法proxy.save();? ? }}

在Spring的AOP編程中:

如果加入容器的目標對象有實現(xiàn)接口,用JDK代理

如果目標對象沒有實現(xiàn)接口,用Cglib代理

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末括蝠,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子乍丈,更是在濱河造成了極大的恐慌,老刑警劉巖繁疤,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件樟蠕,死亡現(xiàn)場離奇詭異钳宪,居然都是意外死亡,警方通過查閱死者的電腦和手機隐砸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進店門之碗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人凰萨,你說我怎么就攤上這事继控。” “怎么了胖眷?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵武通,是天一觀的道長。 經(jīng)常有香客問我珊搀,道長冶忱,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任境析,我火速辦了婚禮囚枪,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘劳淆。我一直安慰自己链沼,他們只是感情好,可當我...
    茶點故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布沛鸵。 她就那樣靜靜地躺著括勺,像睡著了一般。 火紅的嫁衣襯著肌膚如雪曲掰。 梳的紋絲不亂的頭發(fā)上疾捍,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天,我揣著相機與錄音栏妖,去河邊找鬼乱豆。 笑死,一個胖子當著我的面吹牛吊趾,可吹牛的內(nèi)容都是我干的宛裕。 我是一名探鬼主播,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼论泛,長吁一口氣:“原來是場噩夢啊……” “哼揩尸!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起孵奶,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤疲酌,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體朗恳,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡湿颅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了粥诫。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片油航。...
    茶點故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖怀浆,靈堂內(nèi)的尸體忽然破棺而出谊囚,到底是詐尸還是另有隱情,我是刑警寧澤执赡,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布镰踏,位于F島的核電站,受9級特大地震影響沙合,放射性物質(zhì)發(fā)生泄漏奠伪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一首懈、第九天 我趴在偏房一處隱蔽的房頂上張望绊率。 院中可真熱鬧,春花似錦究履、人聲如沸滤否。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽藐俺。三九已至,卻和暖如春盯仪,著一層夾襖步出監(jiān)牢的瞬間紊搪,已是汗流浹背蜜葱。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工全景, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人牵囤。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓爸黄,卻偏偏與公主長得像,于是被迫代替她去往敵國和親揭鳞。 傳聞我的和親對象是個殘疾皇子炕贵,可洞房花燭夜當晚...
    茶點故事閱讀 44,864評論 2 354

推薦閱讀更多精彩內(nèi)容

  • Java的三種代理模式(Spring動態(tài)代理對象) Java的三種代理模式 1.代理模式 代理(Proxy)是一種...
    552163671414閱讀 245評論 0 0
  • 1.代理模式 代理(Proxy)是一種設計模式, 提供了對目標對象另外的訪問方式野崇;即通過代理訪問目標對象称开。 這樣好...
    yangliangliang閱讀 283評論 0 0
  • 代理可以分為靜態(tài)代理、動態(tài)代理,動態(tài)代理又可以分為 jvm的動態(tài)代理 和 cglib的動態(tài)代理鳖轰。像spring框架...
    路遠處幽閱讀 348評論 0 1
  • 1. 代理模式 1.1清酥、代理模式簡單介紹 代理模式定義:給某一個對象提供一個代理,并由代理對象控制對原對象的引用蕴侣。...
    問天036閱讀 275評論 0 0
  • 場景 生產(chǎn)環(huán)境是斷網(wǎng)昆雀,無法聯(lián)網(wǎng)安裝依賴包辱志。 第三方依賴庫太多,每次聯(lián)網(wǎng)安裝依賴包太耗費時間或網(wǎng)絡資源狞膘。 需要更改已...
    SoloSmart閱讀 3,940評論 0 0