設(shè)計模式

1.??? 代理模式

1.1啄糙、代理模式簡單介紹

代理模式定義:給某一個對象提供一個代理,并由代理對象控制對原對象的引用舀瓢。

代理(Proxy)是一種設(shè)計模式,提供了對目標(biāo)對象另外的訪問方式;即通過代理對象訪問目標(biāo)對象.這樣做的好處是:可以在目標(biāo)對象實現(xiàn)的基礎(chǔ)上,增強額外的功能操作,即擴展目標(biāo)對象的功能. 這里使用到編程中的一個思想:不要隨意去修改別人已經(jīng)寫好的代碼或者方法,如果需改修改,可以通過代理的方式來擴展該方法

1.2、代理模式的三個角色

ISubject:抽象主題角色,是一個接口祝峻,該接口目標(biāo)對象和代理對象公用的一個接口。

RealSubject:真實主題角色次坡,是實現(xiàn)抽象主題接口的類呼猪,即目標(biāo)對象。

Proxy: 代理角色砸琅,內(nèi)部含有對真實對象RealSubject的引用宋距,從而可以操作真實對象。代理對象提供與真實對象相同的接口症脂,以便在任何時刻都能代替真實對象谚赎。同時,代理對象可以在執(zhí)行真實對象操作時诱篷,附加其他的操作壶唤,相當(dāng)于對真實對象進行封裝。即代理對象

1.3棕所、靜態(tài)代理

代理模式主要分為兩類闸盔,靜態(tài)代理和動態(tài)代理。靜態(tài)代理比較簡單琳省,迎吵,是由程序員編寫的代理類,并在程序運行前就編譯好的针贬,而不是由程序動態(tài)產(chǎn)生代理類击费,這就是所謂的靜態(tài)。

下面舉個案例來解釋:模擬數(shù)獲取動作,定義一個保存動作的接口:InterfaceProxy.java,然后目標(biāo)對象實現(xiàn)這個接口的方法Data.java,此時如果使用靜態(tài)代理方式,就需要在代理對象(DataProxy.java)中也實現(xiàn)InterfaceProxy接口.調(diào)用的時候通過調(diào)用代理對象的方法來調(diào)用目標(biāo)對象.

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

代碼示例:

接口:InterfaceProxy.java

/**

?*@authoryfli

?*@describe 接口桦他,定義一個獲取數(shù)據(jù)的方法

?*/

public interface InterfaceProxy {

? void getData();

}


目標(biāo)對象:Data.java

/**

?*@authoryfli

?*@describe 目標(biāo)對象蔫巩,實現(xiàn)接口中定義的獲取數(shù)據(jù)方法

?*/

public class Data implements InterfaceProxy{

? public void getData() {

??? System.out.println("-----------數(shù)據(jù)獲取成功----------");

? }

}

代理對象:DataProxy.java

/**

?*@authoryfli

?*@describe 代理對象,同樣實現(xiàn)InterfaceProxy接口

?*/

public class DataProxy implements InterfaceProxy {


? /**

? ?*定義代理目標(biāo)對象

? ?*/

? private Data target;


? /**

? ?*@param target

? ?*/

? public DataProxy(Data target) {

??? this.target = target;

? }


? @Override

? public void getData() {

??? // TODO Auto-generated method stub

??? System.out.println("開始執(zhí)行...");

??????? target.getData();//執(zhí)行目標(biāo)對象的方法

??????? System.out.println("執(zhí)行結(jié)束...");

? }

}

測試類:TestProxy.java

/**

?*@authoryfli

?*@describe 靜態(tài)代理測試類

?*/

public class TestProxy {

? public static void main(String[] args) {

??????? //目標(biāo)對象

??????? Datatarget = new Data();

??????? //代理對象,把目標(biāo)對象傳給代理對象,建立代理關(guān)系

??????? DataProxyproxy = new DataProxy(target);


??????? proxy.getData();//執(zhí)行的是代理的方法

??? }

}


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

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

2.缺點:

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

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


1.4、動態(tài)代理

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

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

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

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

JDK中生成代理對象的API代理類所在包:java.lang.reflect.Proxy

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

static Object newProxyInstance(ClassLoader loader, Class[]interfaces,InvocationHandler h )

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

ClassLoader loader,:指定當(dāng)前目標(biāo)對象使用類加載器,獲取加載器的方法是固定的

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

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

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

代理工廠類:ProxyFactory.java

/**

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

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

?*/

public class ProxyFactory{


??? //維護一個目標(biāo)對象

??? private Object target;

??? public ProxyFactory(Object target){

??????? this.target=target;

??? }


?? //給目標(biāo)對象生成代理對象

??? public Object getProxyInstance(){

??????? returnProxy.newProxyInstance(

??????????????? target.getClass().getClassLoader(),

??????????????? target.getClass().getInterfaces(),

??????????????? new InvocationHandler() {

??????????????????? @Override

??????????????????? public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

??????????????????????? System.out.println("開始執(zhí)行2");

??????????????????????? //執(zhí)行目標(biāo)對象方法

??????????????????????? ObjectreturnValue = method.invoke(target, args);

??????????????????????? System.out.println("執(zhí)行結(jié)束2");

??????????????????????? return returnValue;

??????????????????? }

??????????????? }

??????? );

??? }

}


測試類:TestProxy.java


/**

?*@authoryfli

?*@describe 動態(tài)代理測試類

?*/

public class TestProxy {

? public static void main(String[] args) {

??????? // 目標(biāo)對象

??????? Datatarget = new Data();

??????? // 【原始的類型 class cn.itcast.b_dynamic.UserDao】

??????? System.out.println(target.getClass());

??????? // 給目標(biāo)對象圆仔,創(chuàng)建代理對象

??????? InterfaceProxyproxy = (InterfaceProxy) new ProxyFactory(target).getProxyInstance();

??????? // class $Proxy0?? 內(nèi)存中動態(tài)生成的代理對象

??????? System.out.println(proxy.getClass());

??????? // 執(zhí)行方法?? 【代理對象】

??????? proxy.getData();

??? }

}

總結(jié):代理對象不需要實現(xiàn)接口,但是目標(biāo)對象一定要實現(xiàn)接口,否則不能用動態(tài)代理


1.5垃瞧、Cglib代理

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

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

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.目標(biāo)對象的方法如果為final/static,那么就不會被攔截,即不會執(zhí)行目標(biāo)對象額外的業(yè)務(wù)方法.

示例代碼:

目標(biāo)對象:Data.java

/**

?*@authoryfli

?*@describe 目標(biāo)對象

?*/

public class Data{

? public void getData() {

??? System.out.println("-----------數(shù)據(jù)獲取成功----------");

? }

}


代理工廠:ProxyFactory.java

/**

?*Cglib子類代理工廠

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

?*/

public class ProxyFactory implements MethodInterceptor{

??? //維護目標(biāo)對象

??? private Object target;


??? public ProxyFactory(Object target) {

??????? this.target = target;

??? }


??? //給目標(biāo)對象創(chuàng)建一個代理對象

??? public Object getProxyInstance(){

??????? //1.工具類

??????? Enhanceren = new Enhancer();

??????? //2.設(shè)置父類

??????? en.setSuperclass(target.getClass());

??????? //3.設(shè)置回調(diào)函數(shù)

??????? en.setCallback(this);

??????? //4.創(chuàng)建子類(代理對象)

??????? return en.create();

??? }


??? @Override

??? public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {

??????? System.out.println("開始執(zhí)行...");

??????? //執(zhí)行目標(biāo)對象的方法

??????? ObjectreturnValue = method.invoke(target, args);

??????? System.out.println("執(zhí)行結(jié)束...");

??????? return returnValue;

??? }

}


測試類:TestProxy.java

/**

?*@authoryfli

?*@describe 動態(tài)代理測試類

?*/

public class TestProxy {

? public static void main(String[] args) {

??? //目標(biāo)對象

??????? Datatarget = new Data();

??????? //代理對象

??????? Dataproxy = (Data)new ProxyFactory(target).getProxyInstance();

??????? //執(zhí)行代理對象的方法

??????? proxy.getData();

??? }

}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市荧缘,隨后出現(xiàn)的幾起案子皆警,更是在濱河造成了極大的恐慌,老刑警劉巖截粗,帶你破解...
    沈念sama閱讀 211,348評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件信姓,死亡現(xiàn)場離奇詭異,居然都是意外死亡绸罗,警方通過查閱死者的電腦和手機意推,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來珊蟀,“玉大人菊值,你說我怎么就攤上這事∮模” “怎么了腻窒?”我有些...
    開封第一講書人閱讀 156,936評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長磅崭。 經(jīng)常有香客問我儿子,道長,這世上最難降的妖魔是什么砸喻? 我笑而不...
    開封第一講書人閱讀 56,427評論 1 283
  • 正文 為了忘掉前任柔逼,我火速辦了婚禮,結(jié)果婚禮上割岛,老公的妹妹穿的比我還像新娘愉适。我一直安慰自己,他們只是感情好癣漆,可當(dāng)我...
    茶點故事閱讀 65,467評論 6 385
  • 文/花漫 我一把揭開白布维咸。 她就那樣靜靜地躺著,像睡著了一般惠爽。 火紅的嫁衣襯著肌膚如雪癌蓖。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,785評論 1 290
  • 那天疆股,我揣著相機與錄音,去河邊找鬼倒槐。 笑死旬痹,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播两残,決...
    沈念sama閱讀 38,931評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼永毅,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了人弓?” 一聲冷哼從身側(cè)響起沼死,我...
    開封第一講書人閱讀 37,696評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎崔赌,沒想到半個月后意蛀,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,141評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡健芭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,483評論 2 327
  • 正文 我和宋清朗相戀三年县钥,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片慈迈。...
    茶點故事閱讀 38,625評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡若贮,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出痒留,到底是詐尸還是另有隱情谴麦,我是刑警寧澤,帶...
    沈念sama閱讀 34,291評論 4 329
  • 正文 年R本政府宣布伸头,位于F島的核電站匾效,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏熊锭。R本人自食惡果不足惜弧轧,卻給世界環(huán)境...
    茶點故事閱讀 39,892評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望碗殷。 院中可真熱鬧精绎,春花似錦、人聲如沸锌妻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽仿粹。三九已至搁吓,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間吭历,已是汗流浹背堕仔。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留晌区,地道東北人摩骨。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓通贞,卻偏偏與公主長得像,于是被迫代替她去往敵國和親恼五。 傳聞我的和親對象是個殘疾皇子昌罩,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,492評論 2 348

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

  • 參考資料:菜鳥教程之設(shè)計模式 設(shè)計模式概述 設(shè)計模式(Design pattern)代表了最佳的實踐,通常被有經(jīng)驗...
    Steven1997閱讀 1,169評論 1 12
  • 本文首發(fā)于個人博客:Lam's Blog - 談?wù)?3種設(shè)計模式在Android源碼及項目中的應(yīng)用灾馒,文章由Mark...
    格子林ll閱讀 4,633評論 1 105
  • 1.代理模式 代理(Proxy)是一種設(shè)計模式茎用, 提供了對目標(biāo)對象另外的訪問方式;即通過代理訪問目標(biāo)對象睬罗。 這樣好...
    yangliangliang閱讀 279評論 0 0
  • 為了考研準(zhǔn)備了兩年轨功,最后發(fā)現(xiàn)我居然只是享受那一種感覺而已,曾經(jīng)那么那么渴望能夠延續(xù)我的大學(xué)生活傅物,以校園愛情結(jié)束我2...
    小白碎碎念的記錄閱讀 168評論 0 0
  • 被手機和飛速的網(wǎng)絡(luò)夯辖,閉鎖 周圍,似乎空氣能輕易探出董饰, 一遍遍來回逡巡蒿褂。 點擊不得入,亦不知該入何處去卒暂。 似乎有自由...
    煮熟的河神閱讀 264評論 0 0