設(shè)計模式之代理模式

代理模式(Proxy)為其他對象提供一種代理以控制對這個對象的訪問 易茬。

場景:隨著移動支付業(yè)務(wù)的擴展羞芍,交電話費不在需要跑到營業(yè)廳繳納茉帅,使用支付寶歼捐、微信象泵、網(wǎng)上銀行等網(wǎng)上支付渠道就可以足不出戶輕松繳納電話費邪乍。

角色

  • 抽象主題角色(Subject)?主要用來聲明代理類和真實類的共同接口毅舆。代理類和真實類都需要實現(xiàn)該接口歪今。例如:繳納電話費萧落。

  • 代理主題角色(ProxySubject)践美。(含有對真實主題的引用),通常在調(diào)用真實主題的方法前后加入些其他的操作, 這是目的所在。例如:網(wǎng)上銀行找岖。

  • 真實主題角色(RealSubject)陨倡。需要真實引用的那個角色。例如:繳費人许布。

// Subject抽象主題角色:

public interface class PhonePay {

????????//手機號碼及繳納金額

???????? void pay(String phoneNo兴革,double money);

}

// RealSubject 真實主題角色:

public class SomeonePhonePay implements PhonePay {

???????//手機號碼及繳納金額

???????public void pay(String phoneNo,double money) {

???????????//此處可以調(diào)用運營商充值接口蜜唾。

????????????System.out.println(“ 為手機號為: “ +phoneNo + ” 充值 “+ money);

???????}

}

// ProxySubject代理主題角色:

public class BankPhonePay extends PhonePay {

????private PhonePay someone;

????public void pay(String 杂曲,double money)????{

????????String id = beforRequest(phoneNo,money);

??????????someone.pay(phoneNo袁余,money);

??????????afterRequest(phoneNo擎勘, id);

????}

????private void beforRequest(String phoneNo ,money)????{

??????????????someone = new SomeonePhonePay();

??????????????System.out.println(phoneNo + " login Web Bank");

????????????System.out.println(“ 提取需要充值的轉(zhuǎn)賬金額,并生成流水ID");

????????????return id;

????}

????private void afterRequest(String phoneNo 颖榜, id)????{

????????????System.out.println(“ 依照ID確認(rèn)轉(zhuǎn)賬成功棚饵。");

???????????????System.out.println(phoneNo + " after request,logout");

????}

}

上面一種是普通代理掩完,還有一種強制代理噪漾。強制代理在代理模式中比較另類,一般的思維都是通過代理找到真實的角色且蓬,但是強制代理卻是要“強制” 你必須通過真實角色查找到代理角色欣硼,否則你不能訪問,不管是通過代理類還是通過直接new一個主題角色類恶阴,都不能訪問诈胜,只有通過真實角色指定的代理類才可以訪問豹障,也就是說由真實角色管理代理角色,可以這樣講就是new了一個真實角色的對象耘斩,返回的卻是代理角色沼填。

場景:一天,A找局長辦一件事情括授,但是局長當(dāng)天的行程安排很滿坞笙,只能指定秘書完成。這樣只有秘書才是局長指定的代理荚虚,其他任何人都無法替代薛夜。

public interface Subject{

????void doRequest();

????Subject getProxy(); //指定代理

}

public class ProxySubject implements Subject{

????private Subject proxy = null;

????public ProxySubject(Subject proxy){

???????????this.proxy = proxy;

?????}

?????public void doRequest(){

???????????proxy.doRequest();

??????}

?????// 代理的代理暫時還沒有,就是自己?

?????public Subject getProxy(){

??????????return this;

??????}

}

public class RealSubject implements Subject {

????private Subject proxy = null; // 指定的代理是誰

????public void doRequest(){

????????if (proxy != null){

????????????System.out.println("do Request");

????????} else{

????????????System.out.println("please use proxy,refuse doing this request");

???????}

???}

???public Subject getProxy(){

??????this.proxy = new ProxySubject(this);

??????return proxy;

???}

}

public class Client{

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

??????????Subject playerR = new RealSubject();

??????????playerR.doRequest();// 不可以直接使用 需要由指定的代理完成

??????????playerR.getProxy().doRequest();// 由代理可以完成

???????????// 在代理中即使指定真實對象版述,但是由于不是由真實對象指定的依然不能完成

??????????Subject playerR1 = new RealSubject();

??????????Subject playerP = new ProxySubject(playerR1);

??????????playerP.doRequest();

??????????//代理即使獲取代理依然無法執(zhí)行

??????????playerP.getProxy().doRequest();

?????}

}

代理模式使用場景

  • 遠程代理(Remote Proxy?):該代理可以讓客戶端透明地引用一個存在于不同地址空間(遠程或本地)的對象梯澜。 Java中的RMI技術(shù)是遠程調(diào)用和webservice服務(wù)就是典型的遠程代理模式,客戶并不知道網(wǎng)絡(luò)訪問細節(jié)(訪問本地還是遠程網(wǎng)絡(luò))渴析,代理對象對外屏蔽了網(wǎng)絡(luò)通訊細則晚伙。

  • 保護代理(Protection Proxy)控制對RealSubject對象的訪問〖蠹耄可以實現(xiàn)給不同的用戶提供不同級別的使用權(quán)限咆疗。保護代理可以在運行時間對用戶的有關(guān)權(quán)限進行檢查,然后在核實后決定將調(diào)用傳遞給被代理的對象母债,應(yīng)用于權(quán)限控制午磁。

  • 虛擬代理(Virtual Proxy):該代理允許一個對象只有在真正被用到時才被創(chuàng)建。 hibernate中的懶加載毡们⊙富剩或文檔中有非常大的圖片,為了保證文檔打開速度衙熔,在打開文檔的時候并不真正創(chuàng)建圖片對象登颓,只有當(dāng)用到圖片的時候,才會去創(chuàng)建圖片對象红氯, 百度文庫的預(yù)覽框咙。

  • 防火墻代理(Firewall Proxy):保護目標(biāo)對象不受某些不良客戶端的侵害。

  • 同步代理(Synchronization Proxy): 使幾個用戶能夠同時使用一個對象而沒有沖突脖隶,提供并發(fā)控制扁耐。

  • 對象拷貝延遲代理(Copy-on-Write Proxy):該代理可以延遲一個對象的拷貝操作到客戶調(diào)用里暇检,它是Virtual Proxy模式的一種特殊情況产阱。一般來說,對象的深度克隆是一個高開銷的動作块仆,該代理可以讓這個動作延遲构蹬,只有對象被用到的時候才被克隆王暗。比如,有一個很大的Collection,具體如hashtable,有很多客戶端會并發(fā)同時訪問它庄敛。其中一個特別的客戶端要進行連續(xù)的數(shù)據(jù)獲取,此時由于是針對clone 出來的對象操作,如果原來的母體被其他客戶端操作修改了,那么對clone出來的對象操作就沒有意義了俗壹。可以等其他客戶端修改完成后再進行clone,也就是說,這個特別的客戶端先通過調(diào)用一個叫clone的方法來進行一系列數(shù)據(jù)獲取操作藻烤。但實際上沒有真正的進行對象拷貝,直至有其他客戶端修改了這個對象Collection绷雏。

  • 智能引用代理(Smart Reference Proxy):當(dāng)一個對象被引用時,提供一些額外的操作怖亭,比如將對此對象調(diào)用的次數(shù)記錄下來等涎显。

  • 緩存代理(Cache Proxy): 為創(chuàng)建時高開銷的對象提供緩存,以供多客戶端共享兴猩。如數(shù)據(jù)庫代理期吓,客戶端進行Sql查詢時,并不訪問數(shù)據(jù)庫倾芝,而是通過與數(shù)據(jù)庫代理之間進行交互讨勤,如果數(shù)據(jù)庫代理中沒有緩存數(shù)據(jù),才真正去數(shù)據(jù)庫查詢晨另。

Java實現(xiàn)代理的方式分為靜態(tài)代理和動態(tài)代理潭千,動態(tài)代理又分為基于jdk的動態(tài)代理和基于cglib的動態(tài)代理。

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

//Subject

public interface IUserDao {

????void save();

}

//realSubject

public class UserDao implements IUserDao {

????public void save() {

????????System.out.println("----已經(jīng)保存數(shù)據(jù)!----");

????}

}

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

public class UserDaoProxy implements IUserDao{

????//接收保存目標(biāo)對象

????private IUserDao target;

????public UserDaoProxy(IUserDao target){

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

????}

????public void save() {

????????System.out.println("開始事務(wù)...");

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

????????System.out.println("提交事務(wù)...");

????}

}

public class App {

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

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

????????UserDao target = new UserDao();

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

????????UserDaoProxy proxy = new UserDaoProxy(target);

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

????}

}

優(yōu)點:可以做到在不修改目標(biāo)對象的功能前提下,對目標(biāo)功能在代理類中完成擴展脊岳。

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

JAVA代理機制之基于JDK的動態(tài)代理

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

  • 代理對象,不需要實現(xiàn)接口垛玻。

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

  • 動態(tài)代理也叫做:JDK代理,接口代理帚桩。

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

  • JDK實現(xiàn)代理只需要使用newProxyInstance方法即可账嚎。

  • 通過實現(xiàn)接口的方式 -> JDK動態(tài)代理

//Subject

public interface IUserDao {

????void save();

}

//realSubject

public class UserDao implements IUserDao {

????public void save() {

????????System.out.println("----已經(jīng)保存數(shù)據(jù)!----");

????}

}

public class ProxyFactory{???//proxy不需要繼承接口

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

????private Object target;

????public ProxyFactory(Object target){

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

????}

???

//給目標(biāo)對象生成代理對象莫瞬,并將要實現(xiàn)的動作,原接口的聲明郭蕉,通過匿名類的方式實現(xiàn)疼邀。

????public Object getProxyInstance(){

????????return Proxy.newProxyInstance(target.getClass().getClassLoader(),???

?????????????????????????????????????????????????????target.getClass().getInterfaces(), new InvocationHandler() {

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

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

????????????????????????System.out.println("開始事務(wù)2");

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

????????????????????????Object returnValue = method.invoke(target, args);

????????????????????????System.out.println("提交事務(wù)2");

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

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

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

????????);

????}

}

public class App {

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

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

????????IUserDao target = new UserDao();

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

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

????????IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();

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

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

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

????????proxy.save();

????}

}

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

public class ProxyFactory implements InvocationHandler {???

????//proxy不需要繼承需要代理實現(xiàn)的接口旁振,但需要繼承InvocationHandler類來簡化instance

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

????private Object target;

????public ProxyFactory(Object target){

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

????}

????@Override

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

????????System.out.println("開始事務(wù)2");

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

???????Object returnValue = method.invoke(target, args);

???????System.out.println("提交事務(wù)2");

???????return returnValue;

???}

}

public class App {

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

?????????IUserDao target = new UserDao();

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

????????InvocationHandler subjectProxy = new SubjectProxy(target);

????????IUserDao proxyInstance = (IUserDao)??

????????????????????????????????????????????Proxy.newProxyInstance(subjectProxy.getClass().getClassLoader(),

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

?????????proxyInstance.save();

????}

}

JAVA代理機制之基于CGILB的動態(tài)代理???

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

  • 通過繼承類的方式 -> CGLIB動態(tài)代理

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

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

  • 代理的類不能為final,否則報錯。

  • 目標(biāo)對象的方法如果為final/static,那么就不會被攔截,即不會執(zhí)行目標(biāo)對象額外的業(yè)務(wù)方法拐袜。

//realSubject

public class UserDao {

????//subject簡化成方法而不是接口

????public void save() {

????????System.out.println("----已經(jīng)保存數(shù)據(jù)!----");

????}

}

//proxysbuject

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.工具類

????????Enhancer en = 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("開始事務(wù)...");

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

????????Object returnValue = method.invoke(target, args);

????????System.out.println("提交事務(wù)...");

????????return returnValue;

????}

}

public class App {

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

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

????????UserDao target = new UserDao();

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

????????UserDao proxy = (UserDao)new ProxyFactory(target).getProxyInstance();

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

????????proxy.save();

????}

}


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末吉嚣,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子蹬铺,更是在濱河造成了極大的恐慌尝哆,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,681評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件甜攀,死亡現(xiàn)場離奇詭異秋泄,居然都是意外死亡,警方通過查閱死者的電腦和手機规阀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評論 3 399
  • 文/潘曉璐 我一進店門印衔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人姥敛,你說我怎么就攤上這事奸焙。” “怎么了彤敛?”我有些...
    開封第一講書人閱讀 169,421評論 0 362
  • 文/不壞的土叔 我叫張陵与帆,是天一觀的道長。 經(jīng)常有香客問我墨榄,道長玄糟,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,114評論 1 300
  • 正文 為了忘掉前任袄秩,我火速辦了婚禮阵翎,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘之剧。我一直安慰自己郭卫,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 69,116評論 6 398
  • 文/花漫 我一把揭開白布背稼。 她就那樣靜靜地躺著贰军,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蟹肘。 梳的紋絲不亂的頭發(fā)上词疼,一...
    開封第一講書人閱讀 52,713評論 1 312
  • 那天,我揣著相機與錄音帘腹,去河邊找鬼贰盗。 笑死,一個胖子當(dāng)著我的面吹牛阳欲,可吹牛的內(nèi)容都是我干的舵盈。 我是一名探鬼主播米辐,決...
    沈念sama閱讀 41,170評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼书释!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起赊窥,我...
    開封第一講書人閱讀 40,116評論 0 277
  • 序言:老撾萬榮一對情侶失蹤爆惧,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后锨能,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體扯再,經(jīng)...
    沈念sama閱讀 46,651評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,714評論 3 342
  • 正文 我和宋清朗相戀三年址遇,在試婚紗的時候發(fā)現(xiàn)自己被綠了熄阻。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,865評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡倔约,死狀恐怖秃殉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情浸剩,我是刑警寧澤钾军,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站绢要,受9級特大地震影響吏恭,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜重罪,卻給世界環(huán)境...
    茶點故事閱讀 42,211評論 3 336
  • 文/蒙蒙 一樱哼、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧剿配,春花似錦搅幅、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至砸讳,卻和暖如春琢融,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背簿寂。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評論 1 274
  • 我被黑心中介騙來泰國打工漾抬, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人常遂。 一個月前我還...
    沈念sama閱讀 49,299評論 3 379
  • 正文 我出身青樓纳令,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子平绩,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,870評論 2 361

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

  • 代理模式的定義:代理模式給某一個對象提供一個代理對象圈匆,并由代理對象控制對 原對象 的引用。 通俗的來講代理...
    代碼之尖閱讀 701評論 0 49
  • 代理模式,今天我們就來說說代理模式,簡單點來說就是相當(dāng)于明星的經(jīng)紀(jì)人一樣,一般沒有執(zhí)行訪問明星的, 都是通過訪問其...
    子龍神閱讀 173評論 0 1
  • 代理模式的定義:為對象提供一種代理用來控制這個對象的訪問捏雌。 代理模式的主要構(gòu)成有三部分: 1跃赚、抽象對象 該對象是目...
    超級大雞腿閱讀 163評論 0 0
  • ??今天給大家分享的是java設(shè)計模式之代理模式中的靜態(tài)代理模式,動態(tài)代理模式將在后面文章中給出性湿。如有不足纬傲,敬請指...
    架構(gòu)師Javaspring閱讀 298評論 0 0
  • 久違的晴天,家長會肤频。 家長大會開好到教室時叹括,離放學(xué)已經(jīng)沒多少時間了。班主任說已經(jīng)安排了三個家長分享經(jīng)驗宵荒。 放學(xué)鈴聲...
    飄雪兒5閱讀 7,528評論 16 22