12.4 代理模式的擴展
12.4.1 普通代理
在網絡上代理服務器設置分為透明代理和普通代理,是什么意思呢?透明代理就是用戶不用設置代理服務器地址,就可以直接訪問,也就是說代理服務器對用戶來說是透明的,不用知道它存在的;普通代理則是需要用戶自己設置代理服務器的IP地址,用戶必須知道代理的存在。我們設計模式中的普通代理和強制代理也是類似的一種結構,普通代理就是我們要知道代理的存在,也就是類似的GamePlayerProxy這個類的存在,然后才能訪問;強制代理則是調用者直接調用真實角色,而不用關心代理是否存在,其代理的產生是由真實角色決定的,這樣的解釋還是比較復雜,我們還是用實例來講解。
首先說普通代理,它的要求就是客戶端只能訪問代理角色,而不能訪問真實角色,這是比較簡單的。我們以上面的例子作為擴展,我自己作為一個游戲玩家,我肯定自己不練級了,也就是場景類不能再直接new一個GamePlayer對象了,它必須由GamePlayerProxy來進行模擬場景,類圖修改如圖12-4所示。

圖12-4 普通代理類圖
改動很小,僅僅修改了兩個實現類的構造函數,GamePlayer的構造函數增加了_gamePlayer參數,而代理角色則只要傳入代理者名字即可,而不需要說是替哪個對象做代理。GamePlayer類如代碼清單12-10所示。
代碼清單12-10 普通代理的游戲者
public?class?GamePlayer?implements?IGamePlayer?{
?????private?String?name?=?"";??
?????//構造函數限制誰能創建對象,并同時傳遞姓名
?????public?GamePlayer(IGamePlayer?_gamePlayer,String?_name)?throws?Exception{
?????????????if(_gamePlayer?==?null?){
????????????????????throw?new?Exception("不能創建真實角色!");
?????????????}else{
????????????????????this.name?=?_name;
?????????????}
?????}
?????//打怪,最期望的就是殺老怪
?????public?void?killBoss()?{
?????????????System.out.println(this.name?+?"在打怪!");
?????}
?????//進游戲之前你肯定要登錄吧,這是一個必要條件
?????public?void?login(String?user,?String?password)?{
?????????????System.out.println("登錄名為"+user?+?"的用戶"?+?this.name?+?"登錄成功!");
?????}
?????//升級,升級有很多方法,花錢買是一種,做任務也是一種
?????public?void?upgrade()?{
?????????????System.out.println(this.name?+?"?又升了一級!");
?????}
}
在構造函數中,傳遞進來一個IGamePlayer對象,檢查誰能創建真實的角色,當然還可以有其他的限制,比如類名必須為Proxy類等,讀者可以根據實際情況進行擴展。GamePlayerProxy如代碼清單12-11所示。
代碼清單12-11 普通代理的代理者
public?class?GamePlayerProxy?implements?IGamePlayer?{
?????private?IGamePlayer?gamePlayer?=?null;
?????//通過構造函數傳遞要對誰進行代練
?????public?GamePlayerProxy(String?name){
?????????????try?{
????????????????????gamePlayer?=?new?GamePlayer(this,name);
?????????????}?catch?(Exception?e)?{
????????????????????//?TODO?異常處理
?????????????}
?????}
?????//代練殺怪
?????public?void?killBoss()?{
?????????????this.gamePlayer.killBoss();
?????}
?????//代練登錄
?????public?void?login(String?user,?String?password)?{
?????????????this.gamePlayer.login(user,?password);
?????}
?????//代練升級
?????public?void?upgrade()?{
?????????????this.gamePlayer.upgrade();
?????}
}
僅僅修改了構造函數,傳遞進來一個代理者名稱,即可進行代理,在這種改造下,系統更加簡潔了,調用者只知道代理存在就可以,不用知道代理了誰。同時場景類也稍作改動,如代碼清單12-12所示。
代碼清單12-12 普通代理的場景類
public?class?Client?{
?????public?static?void?main(String[]?args)?{
?????????????//然后再定義一個代練者
?????????????IGamePlayer?proxy?=?new?GamePlayerProxy("張三");
?????????????//開始打游戲,記下時間戳
?????????????System.out.println("開始時間是:2009-8-25?10:45");
?????????????proxy.login("zhangSan",?"password");
?????????????//開始殺怪
?????????????proxy.killBoss();
?????????????//升級
?????????????proxy.upgrade();
?????????????//記錄結束游戲時間
?????????????System.out.println("結束時間是:2009-8-26?03:40");
?????}
}
運行結果完全相同。在該模式下,調用者只知代理而不用知道真實的角色是誰,屏蔽了真實角色的變更對高層模塊的影響,真實的主題角色想怎么修改就怎么修改,對高層次的模塊沒有任何的影響,只要你實現了接口所對應的方法,該模式非常適合對擴展性要求較高的場合。當然,在實際的項目中,一般都是通過約定來禁止new一個真實的角色,這也是一個非常好的方案。
注意 普通代理模式的約束問題,盡量通過團隊內的編程規范類約束,因為每一個主題類是可被重用的和可維護的,使用技術約束的方式對系統維護是一種非常不利的因素。
12.4.2 強制代理
強制代理在設計模式中比較另類,為什么這么說呢?一般的思維都是通過代理找到真實的角色,但是強制代理卻是要“強制”,你必須通過真實角色查找到代理角色,否則你不能訪問。甭管你是通過代理類還是通過直接new一個主題角色類,都不能訪問,只有通過真實角色指定的代理類才可以訪問,也就是說由真實角色管理代理角色。這么說吧,高層模塊new了一個真實角色的對象,返回的卻是代理角色,這就好比是你和一個明星比較熟,相互認識,有件事情你需要向她確認一下,于是你就直接撥通了明星的電話:
“喂,沙比呀,我要見一下×××導演,你幫下忙了!”
“不行呀衰哥,我這幾天很忙呀,你找我的經紀人吧……”
郁悶了吧,你是想直接繞過她的代理,誰知道返回的還是她的代理,這就是強制代理,你可以不用知道代理存在,但是你的所作所為還是需要代理為你提供。我們把上面的例子稍作修改就可以完成,如圖12-5所示。

圖12-5 強制代理類圖
在接口上增加了一個getProxy方法,真實角色GamePlayer可以指定一個自己的代理,除了代理外誰都不能訪問。我們來看代碼,先看IGamePlayer接口,如代碼清單12-13所示。
代碼清單12-13 強制代理的接口類
public?interface?IGamePlayer?{
?????//登錄游戲
?????public?void?login(String?user,String?password);
?????//殺怪,這是網絡游戲的主要特色
?????public?void?killBoss();
?????//升級
?????public?void?upgrade();
?????//每個人都可以找一下自己的代理
?????public?IGamePlayer?getProxy();
}
僅僅增加了一個getProxy方法,指定要訪問自己必須通過哪個代理,實現類也要做適當的修改,先看真實角色GamePlayer,如代碼清單12-14所示。
代碼清單12-14 強制代理的真實角色
public?class?GamePlayer?implements?IGamePlayer?{
?????private?String?name?=?"";
?????//我的代理是誰
?????private?IGamePlayer?proxy?=?null;
?????public?GamePlayer(String?_name){
?????????????this.name?=?_name;?
?????}
?????//找到自己的代理
?????public?IGamePlayer?getProxy(){
?????????????this.proxy?=?new?GamePlayerProxy(this);
?????????????return?this.proxy;
?????}
?????//打怪,最期望的就是殺老怪
?????public?void?killBoss()?{
?????????????if(this.isProxy()){
?????????????????????System.out.println(this.name?+?"在打怪!");
?????????????}else{
?????????????????????System.out.println("請使用指定的代理訪問");
?????????????}
?????}
?????//進游戲之前你肯定要登錄吧,這是一個必要條件
?????public?void?login(String?user,?String?password)?{
?????????????if(this.isProxy()){
?????????????????????System.out.println("登錄名為"+user+"的用戶"+this.name+"登錄成功!");
?????????????}else{
?????????????????????System.out.println("請使用指定的代理訪問");;
??????????}
?????}
?????//升級,升級有很多方法,花錢買是一種,做任務也是一種
?????public?void?upgrade()?{
?????????????if(this.isProxy()){
?????????????????????System.out.println(this.name?+?"?又升了一級!");
?????????????}else{
?????????????????????System.out.println("請使用指定的代理訪問");
?????????????}
?????}
?????//校驗是否是代理訪問
?????private?boolean?isProxy(){
?????????????if(this.proxy?==?null){
?????????????????????return?false;
?????????????}else{
?????????????????????return?true;
?????????????}
?????}
}
增加了一個私有方法,檢查是否是自己指定的代理,是指定的代理則允許訪問,否則不允許訪問。我們再來看代理角色,如代碼清單12-15所示。
代碼清單12-15 強制代理的代理類
public?class?GamePlayerProxy?implements?IGamePlayer?{
?????private?IGamePlayer?gamePlayer?=?null;
?????//構造函數傳遞用戶名
?????public?GamePlayerProxy(IGamePlayer?_gamePlayer){
?????????????this.gamePlayer?=?_gamePlayer;
?????}
?????//代練殺怪
?????public?void?killBoss()?{
?????????????this.gamePlayer.killBoss();
?????}
?????//代練登錄
?????public?void?login(String?user,?String?password)?{
?????????????this.gamePlayer.login(user,?password);
?????}
?????//代練升級
?????public?void?upgrade()?{
?????????????this.gamePlayer.upgrade();
?????}
?????//代理的代理暫時還沒有,就是自己
?????public?IGamePlayer?getProxy(){
?????????????return?this;
?????}
}
代理角色也可以再次被代理,這里我們就沒有繼續延伸下去了,查找代理的方法就返回自己的實例。代碼都寫完畢了,我們先按照常規的思路來運行一下,直接new一個真實角色,如代碼清單12-16所示。
代碼清單12-16 直接訪問真實角色
public?class?Client?{
?????public?static?void?main(String[]?args)?{
?????????????//定義一個游戲的角色
?????????????IGamePlayer?player?=?new?GamePlayer("張三");?????????
?????????????//開始打游戲,記下時間戳
?????????????System.out.println("開始時間是:2009-8-25?10:45");
?????????????player.login("zhangSan",?"password");
?????????????//開始殺怪
?????????????player.killBoss();
?????????????//升級
?????????????player.upgrade();
?????????????//記錄結束游戲時間
?????????????System.out.println("結束時間是:2009-8-26?03:40");
?????}
}
想想看能運行嗎?運行結果如下所示:
開始時間是:2009-8-25 10:45
請使用指定的代理訪問
請使用指定的代理訪問
請使用指定的代理訪問
結束時間是:2009-8-26 03:40
它要求你必須通過代理來訪問,你想要直接訪問它,門兒都沒有,好,你要我通過代理來訪問,那就生產一個代理,如代碼清單12-17所示。
代碼清單12-17 直接訪問代理類
public?class?Client?{
?????public?static?void?main(String[]?args)?{
?????????????//定義一個游戲的角色
?????????????IGamePlayer?player?=?new?GamePlayer("張三");
?????????????//然后再定義一個代練者
?????????????IGamePlayer?proxy?=?new?GamePlayerProxy(player);
?????????????//開始打游戲,記下時間戳
?????????????System.out.println("開始時間是:2009-8-25?10:45");
?????????????proxy.login("zhangSan",?"password");
?????????????//開始殺怪
?????????????proxy.killBoss();
?????????????//升級
?????????????proxy.upgrade();
?????????????//記錄結束游戲時間
?????????????System.out.println("結束時間是:2009-8-26?03:40");
?????}
}
這次能訪問嗎?還是不行,結果如下所示:
開始時間是:2009-8-25 10:45
請使用指定的代理訪問
請使用指定的代理訪問
請使用指定的代理訪問
結束時間是:2009-8-26 03:40
還是不能訪問,為什么呢?它不是真實角色指定的對象,這個代理對象是你自己new出來的,當然真實對象不認了,這就好比是那個明星,人家已經告訴你去找她的代理人了,你隨便找個代理人能成嗎?你必須去找她指定的代理才成!我們修改一下場景類,如代碼清單12-18所示。
代碼清單12-18 強制代理的場景類
public?class?Client?{
?????public?static?void?main(String[]?args)?{
?????????????//定義一個游戲的角色
?????????????IGamePlayer?player?=?new?GamePlayer("張三");
?????????????//獲得指定的代理
?????????????IGamePlayer?proxy?=?player.getProxy();
?????????????//開始打游戲,記下時間戳
?????????????System.out.println("開始時間是:2009-8-25?10:45");
?????????????proxy.login("zhangSan",?"password");
?????????????//開始殺怪
?????????????proxy.killBoss();
?????????????//升級
?????????????proxy.upgrade();
?????????????//記錄結束游戲時間
?????????????System.out.println("結束時間是:2009-8-26?03:40");
?????}
}
運行結果如下:
開始時間是:2009-8-25 10:45
登錄名為zhangSan 的用戶張三登錄成功!
張三在打怪!
張三 又升了一級!
結束時間是:2009-8-26 03:40
OK,可以正常訪問代理了。強制代理的概念就是要從真實角色查找到代理角色,不允許直接訪問真實角色。高層模塊只要調用getProxy就可以訪問真實角色的所有方法,它根本就不需要產生一個代理出來,代理的管理已經由真實角色自己完成。
12.4.3 代理是有個性的
一個類可以實現多個接口,完成不同任務的整合。也就是說代理類不僅僅可以實現主題接口,也可以實現其他接口完成不同的任務,而且代理的目的是在目標對象方法的基礎上作增強,這種增強的本質通常就是對目標對象的方法進行攔截和過濾。例如游戲代理是需要收費的,升一級需要5元錢,這個計算功能就是代理類的個性,它應該在代理的接口中定義,如圖12-6所示。

圖12-6 代理類的個性
增加了一個IProxy接口,其作用是計算代理的費用。我們先來看IProxy接口,如代碼清單12-19所示。
代碼清單12-19 代理類的接口
public?interface?IProxy?{
?????//計算費用
?????public?void?count();
}
僅僅一個方法,非常簡單,看GamePlayerProxy帶來的變化,如代碼清單12-20所示。
代碼清單12-20 代理類
public?class?GamePlayerProxy?implements?IGamePlayer,IProxy?{
?????private?IGamePlayer?gamePlayer?=?null;
?????//通過構造函數傳遞要對誰進行代練
?????public?GamePlayerProxy(IGamePlayer?_gamePlayer){
?????????????this.gamePlayer?=?_gamePlayer;
?????}
?????//代練殺怪
?????public?void?killBoss()?{
?????????????this.gamePlayer.killBoss();
?????}
?????//代練登錄
?????public?void?login(String?user,?String?password)?{
?????????????this.gamePlayer.login(user,?password);
?????}
?????//代練升級
?????public?void?upgrade()?{
?????????????this.gamePlayer.upgrade();
?????????????this.count();
?????}
?????//計算費用
?????public?void?count(){
?????????????System.out.println("升級總費用是:150元");
?????}
}
實現了IProxy接口,同時在upgrade方法中調用該方法,完成費用結算,其他的類都沒有任何改動,運行結果如下:
開始時間是:2009-8-25 10:45
登錄名為zhangSan 的用戶 張三登錄成功!
張三在打怪!
張三 又升了一級!
升級總費用是:150元
結束時間是:2009-8-26 03:40
好了,代理公司也賺錢了,我的游戲也升級了,皆大歡喜。代理類不僅僅是可以有自己的運算方法,通常的情況下代理的職責并不一定單一,它可以組合其他的真實角色,也可以實現自己的職責,比如計算費用。代理類可以為真實角色預處理消息、過濾消息、消息轉發、事后處理消息等功能。當然一個代理類,可以代理多個真實角色,并且真實角色之間可以有耦合關系,讀者可以自行擴展一下。
12.4.4 動態代理
放在最后講的一般都是壓軸大戲,動態代理就是如此,上面的章節都是一個引子,動態代理才是重頭戲。什么是動態代理?動態代理是在實現階段不用關心代理誰,而在運行階段才指定代理哪一個對象。相對來說,自己寫代理類的方式就是靜態代理。本章節的核心部分就在動態代理上,現在有一個非常流行的名稱叫做面向橫切面編程,也就是AOP(Aspect Oriented Programming),其核心就是采用了動態代理機制,既然這么重要,我們就來看看動態代理是如何實現的,還是以打游戲為例,類圖修改一下以實現動態代理,如圖12-7所示。

圖12-7 動態代理
在類圖中增加了一個InvocationHandler接口和GamePlayIH類,作用就是產生一個對象的代理對象,其中InvocationHandler是JDK提供的動態代理接口,對被代理類的方法進行代理。我們來看程序,接口保持不變,實現類也沒有變化,請參考代碼清單12-1和代碼清單12-2所示。我們來看DynamicProxy類,如代碼清單12-21所示。
代碼清單12-21 動態代理類
public?class?GamePlayIH?implements?InvocationHandler?{
?????//被代理者
?????Class?cls?=null;
?????//被代理的實例
?????Object?obj?=?null;
?????//我要代理誰
?????public?GamePlayIH(Object?_obj){
?????????????this.obj?=?_obj;
?????}
?????//調用被代理的方法
?????public?Object?invoke(Object?proxy,?Method?method,?Object[]?args)
????????????????????throws?Throwable?{
?????????????Object?result?=?method.invoke(this.obj,?args);
?????????????return?result;
?????}
}
其中invoke方法是接口InvocationHandler定義必須實現的,它完成對真實方法的調用。我們來詳細講解一下InvocationHandler接口,動態代理是根據被代理的接口生成所有的方法,也就是說給定一個接口,動態代理會宣稱“我已經實現該接口下的所有方法了”,那各位讀者想想看,動態代理怎么才能實現被代理接口中的方法呢?默認情況下所有的方法返回值都是空的,是的,代理已經實現它了,但是沒有任何的邏輯含義,那怎么辦?好辦,通過InvocationHandler接口,所有方法都由該Handler來進行處理,即所有被代理的方法都由InvocationHandler接管實際的處理任務。
我們接下來看看場景類,如代碼清單12-22所示。
代碼清單12-22 動態代理的場景類
public?class?Client?{
?????public?static?void?main(String[]?args)?throws?Throwable??{
?????????????//定義一個癡迷的玩家
?????????????IGamePlayer?player?=?new?GamePlayer("張三");
?????????????//定義一個handler
?????????????InvocationHandler?handler?=?new?GamePlayIH(player);
?????????????//開始打游戲,記下時間戳
?????????????System.out.println("開始時間是:2009-8-25?10:45");
?????????????//獲得類的class?loader
?????????????ClassLoader?cl?=?player.getClass().getClassLoader();
?????????????//動態產生一個代理者
?????????????IGamePlayer?proxy?=?(IGamePlayer)Proxy.newProxyInstance(cl,new?Class[]{IGamePlayer.class},handler);
?????????????//登錄
?????????????proxy.login("zhangSan",?"password");
?????????????//開始殺怪
?????????????proxy.killBoss();
?????????????//升級
?????????????proxy.upgrade();
?????????????//記錄結束游戲時間
?????????????System.out.println("結束時間是:2009-8-26?03:40");
?????}
}
很奇怪是嗎?不要著急,繼續看下去。其運行結果如下:
開始時間是:2009-8-25 10:45
登錄名為zhangSan 的用戶 張三登錄成功!
張三在打怪!
張三 又升了一級!
結束時間是:2009-8-26 03:40
我們還是讓代練者幫我們打游戲,但是我們既沒有創建代理類,也沒有實現IGamePlayer接口,這就是動態代理。別急,動態代理可不僅僅就這么多內容,還有更重要的,如果想讓游戲登錄后發一個信息給我們,防止賬號被人盜用嘛,該怎么處理?直接修改被代理類GamePlayer?這不是一個好辦法,好辦法如代碼清單12-23所示。
代碼清單12-23 修正后的動態代理
public?class?GamePlayIH?implements?InvocationHandler?{
?????//被代理者
?????Class?cls?=null;
?????//被代理的實例
?????Object?obj?=?null;?
?????//我要代理誰
?????public?GamePlayIH(Object?_obj){
?????????????this.obj?=?_obj;
?????}
?????//調用被代理的方法
?????public?Object?invoke(Object?proxy,?Method?method,?Object[]?args)
?????????????????????throws?Throwable?{
?????????????Object?result?=?method.invoke(this.obj,?args);
?????????????//如果是登錄方法,則發送信息
?????????????if(method.getName().equalsIgnoreCase("login")){
?????????????????????System.out.println("有人在用我的賬號登錄!");
?????????????}
?????????????return?result;
?????}
}
看粗體部分,只要在代理中增加一個判斷就可以決定是否要發送信息,運行結果如下:
開始時間是:2009-8-25 10:45
登錄名為zhangSan的用戶 張三登錄成功!
有人在用我的賬號登錄!
張三在打怪!
張三 又升了一級!
結束時間是:2009-8-26 03:40
太棒了!有人用我的賬號就發送一個信息,然后看看自己的賬號是不是被人盜了,非常好的方法,這就是AOP編程。AOP編程沒有使用什么新的技術,但是它對我們的設計、編碼有非常大的影響,對于日志、事務、權限等都可以在系統設計階段不用考慮,而在設計后通過AOP的方式切過去。既然動態代理是如此誘人,我們來看看通用動態代理模型,類圖如圖12-8所示。

圖12-8 動態代理通用類圖
很簡單,兩條獨立發展的線路。動態代理實現代理的職責,業務邏輯Subject實現相關的邏輯功能,兩者之間沒有必然的相互耦合的關系。通知Advice從另一個切面切入,最終在高層模塊也就是Client進行耦合,完成邏輯的封裝任務。我們先來看Subject接口,如代碼清單12-24所示。
代碼清單12-24 抽象主題
public?interface?Subject?{
?????//業務操作
?????public?void?doSomething(String?str);
}
其中的doSomething是一種標識方法,可以有多個邏輯處理方法,實現類如代碼清單12-25所示。
代碼清單12-25 真實主題
public?class?RealSubject?implements?Subject?{
?????//業務操作
?????public?void?doSomething(String?str)?{
?????????????System.out.println("do?something!---->"?+?str);
?????}
}
重點是我們的MyInvocationHandler,如代碼清單12-26所示。
代碼清單12-26 動態代理的Handler類
public?class?MyInvocationHandler?implements?InvocationHandler?{
?????//被代理的對象
?????private?Object?target?=?null;
?????//通過構造函數傳遞一個對象
?????public?MyInvocationHandler(Object?_obj){
?????????????this.target?=?_obj;
?????}
?????//代理方法?????
?????public?Object?invoke(Object?proxy,?Method?method,?Object[]?args)
????????????????????throws?Throwable?{
?????????????//執行被代理的方法
?????????????return?method.invoke(this.target,?args);
?????}
}
非常簡單,所有通過動態代理實現的方法全部通過invoke方法調用。DynamicProxy代碼如代碼清單12-27所示。
代碼清單12-27 動態代理類
public?class?DynamicProxy<T>?{
?????public?static?<T>?T?newProxyInstance(ClassLoader?loader,?Class<?>[]?interfaces,?InvocationHandler?h){
?????????????//尋找JoinPoint連接點,AOP框架使用元數據定義
?????????????if(true){
????????????????????//執行一個前置通知
????????????????????(new?BeforeAdvice()).exec();
?????????????}
?????????????//執行目標,并返回結果
?????????????return?(T)Proxy.newProxyInstance(loader,interfaces,?h);????????????
?????}
}
在這里插入了較多的AOP術語,如在什么地方(連接點)執行什么行為(通知)。我們在這里實現了一個簡單的橫切面編程,有經驗的讀者可以看看AOP的配置文件就會明白這段代碼的意義了。我們來看通知Advice,也就是我們要切入的類,接口和實現如代碼清單12-28所示。
代碼清單12-28 通知接口及實現
public?interface?IAdvice?{?
?????//通知只有一個方法,執行即可
?????public?void?exec();
}
public?class?BeforeAdvice?implements?IAdvice{
?????public?void?exec(){
?????????????System.out.println("我是前置通知,我被執行了!");
?????}
}
最后就是看我們怎么調用了,如代碼清單12-29所示。
代碼清單12-29 動態代理的場景類
public?class?Client?{
?????public?static?void?main(String[]?args)?{
?????????????//定義一個主題
?????????????Subject?subject?=?new?RealSubject();
?????????????//定義一個Handler
?????????????InvocationHandler?handler?=?new?MyInvocationHandler(subject);
?????????????//定義主題的代理
?????????????Subject?proxy?=?DynamicProxy.newProxyInstance(subject.getClass().
???????????????getClassLoader(),?subject.getClass().getInterfaces(),handler);
?????????????//代理的行為
?????????????proxy.doSomething("Finish");
?????}
}
運行結果如下所示:
我是前置通知,我被執行了!
do something!---->Finish
好,所有的程序都看完了,我們回過頭來看看程序是怎么實現的。在DynamicProxy類中,我們有這樣的方法:
this.obj=Proxy.newProxyInstance(c.getClassLoader(),c.getInterfaces(),new MyInvocationHandler(_obj));
該方法是重新生成了一個對象,為什么要重新生成?你要使用代理呀,注意c.getInterfaces()這句話,這是非常有意思的一句話,是說查找到該類的所有接口,然后實現接口的所有方法。當然了,方法都是空的,由誰具體負責接管呢?是new MyInvocationHandler(_Obj)這個對象。于是我們知道一個類的動態代理類是這樣的一個類,由InvocationHandler的實現類實現所有的方法,由其invoke方法接管所有方法的實現,其動態調用過程如圖12-9所示。

圖12-9 動態代理調用過程示意圖
讀者可能注意到我們以上的代碼還有更進一步的擴展余地,注意看DynamicProxy類,它是一個通用類,不具有業務意義,如果我們再產生一個實現類是不是就很有意義了呢?如代碼清單12-30所示。
代碼清單12-30 具體業務的動態代理
public?class?SubjectDynamicProxy?extends?DynamicProxy{
?????public?static?<T>?T?newProxyInstance(Subject?subject){
?????????????//獲得ClassLoader
?????????????ClassLoader?loader?=?subject.getClass().getClassLoader();
?????????????//獲得接口數組
?????????????Class<?>[]?classes?=?subject.getClass().getInterfaces();
?????????????//獲得handler
?????????????InvocationHandler?handler?=?new?MyInvocationHandler(subject);
?????????????return?newProxyInstance(loader,?classes,?handler);
?????}
}
如此擴展以后,高層模塊對代理的訪問會更加簡單,如代碼清單12-31所示。
代碼清單12-31 場景類
public?class?Client?{
?????public?static?void?main(String[]?args)?{
?????????????//定義一個主題
?????????????Subject?subject?=?new?RealSubject();
?????????????//定義主題的代理
?????????????Subject?proxy?=?SubjectDynamicProxy.newProxyInstance(subject);
?????????????//代理的行為
?????????????proxy.doSomething("Finish");
?????}
}
是不是更加簡單了?可能讀者就要提問了,這樣與靜態代理還有什么區別?都是需要實現一個代理類,有區別,注意看父類,動態代理的主要意圖就是解決我們常說的“審計”問題,也就是橫切面編程,在不改變我們已有代碼結構的情況下增強或控制對象的行為。
注意 要實現動態代理的首要條件是:被代理類必須實現一個接口,回想一下前面的分析吧。當然了,現在也有很多技術如CGLIB可以實現不需要接口也可以實現動態代理的方式。
再次說明,以上的動態代理是一個通用代理框架。如果你想設計自己的AOP框架,完全可以在此基礎上擴展,我們設計的是一個通用代理,只要有一個接口,一個實現類,就可以使用該代理,完成代理的所有功效。
- 前言
- 第一部分 大旗不揮,誰敢沖鋒——6大設計原則全新解讀
- 第1章 單一職責原則
- 1.2 絕殺技,打破你的傳統思維
- 1.3 我單純,所以我快樂
- 1.4 最佳實踐
- 第2章 里氏替換原則
- 2.2 糾紛不斷,規則壓制
- 2.3 最佳實踐
- 第3章 依賴倒置原則
- 3.2 言而無信,你太需要契約
- 3.3 依賴的三種寫法
- 3.4 最佳實踐
- 第4章 接口隔離原則
- 4.2 美女何其多,觀點各不同
- 4.3 保證接口的純潔性
- 4.4 最佳實踐
- 第5章 迪米特法則
- 5.2 我的知識你知道得越少越好
- 5.3 最佳實踐
- 第6章 開閉原則
- 6.2 開閉原則的廬山真面目
- 6.3 為什么要采用開閉原則
- 6.4 如何使用開閉原則
- 6.5 最佳實踐
- 第二部分 真刀實槍 ——23種設計模式完美演繹
- 第7章 單例模式
- 7.2 單例模式的定義
- 7.3 單例模式的應用
- 7.4 單例模式的擴展
- 7.5 最佳實踐
- 第8章 工廠方法模式
- 8.2 工廠方法模式的定義
- 8.3 工廠方法模式的應用
- 8.4 工廠方法模式的擴展
- 8.5 最佳實踐
- 第9章 抽象工廠模式
- 9.2 抽象工廠模式的定義
- 9.3 抽象工廠模式的應用
- 9.4 最佳實踐
- 第10章 模板方法模式
- 10.2 模板方法模式的定義
- 10.3 模板方法模式的應用
- 10.4 模板方法模式的擴展
- 10.5 最佳實踐
- 第11章 建造者模式
- 11.2 建造者模式的定義
- 11.3 建造者模式的應用
- 11.4 建造者模式的擴展
- 11.5 最佳實踐
- 第12章 代理模式
- 12.2 代理模式的定義
- 12.3 代理模式的應用
- 12.4 代理模式的擴展
- 12.5 最佳實踐
- 第13章 原型模式
- 13.2 原型模式的定義
- 13.3 原型模式的應用
- 13.4 原型模式的注意事項
- 13.5 最佳實踐
- 第14章 中介者模式
- 14.2 中介者模式的定義
- 14.3 中介者模式的應用
- 14.4 中介者模式的實際應用
- 14.5 最佳實踐
- 第15章 命令模式
- 15.2 命令模式的定義
- 15.3 命令模式的應用
- 15.4 命令模式的擴展
- 15.5 最佳實踐
- 第16章 責任鏈模式
- 16.2 責任鏈模式的定義
- 16.3 責任鏈模式的應用
- 16.4 最佳實踐
- 第17章 裝飾模式
- 17.2 裝飾模式的定義
- 17.3 裝飾模式應用
- 17.4 最佳實踐
- 第18章 策略模式
- 18.2 策略模式的定義
- 18.3 策略模式的應用
- 18.4 策略模式的擴展
- 18.5 最佳實踐
- 第19章 適配器模式
- 19.2 適配器模式的定義
- 19.3 適配器模式的應用
- 19.4 適配器模式的擴展
- 19.5 最佳實踐
- 第20章 迭代器模式
- 20.2 迭代器模式的定義
- 20.3 迭代器模式的應用
- 20.4 最佳實踐
- 第21章 組合模式
- 21.2 組合模式的定義
- 21.3 組合模式的應用
- 21.4 組合模式的擴展
- 21.5 最佳實踐
- 第22章 觀察者模式
- 22.2 觀察者模式的定義
- 22.3 觀察者模式的應用
- 22.4 觀察者模式的擴展
- 22.5 最佳實踐
- 第23章 門面模式
- 23.2 門面模式的定義
- 23.3 門面模式的應用
- 23.4 門面模式的注意事項
- 23.5 最佳實踐
- 第24章 備忘錄模式
- 24.2 備忘錄模式的定義
- 24.3 備忘錄模式的應用
- 24.4 備忘錄模式的擴展
- 24.5 最佳實踐
- 第25章 訪問者模式
- 25.2 訪問者模式的定義
- 25.3 訪問者模式的應用
- 25.4 訪問者模式的擴展
- 25.5 最佳實踐
- 第26章 狀態模式
- 26.2 狀態模式的定義
- 26.3 狀態模式的應用
- 第27章 解釋器模式
- 27.2 解釋器模式的定義
- 27.3 解釋器模式的應用
- 27.4 最佳實踐
- 第28章 享元模式
- 28.2 享元模式的定義
- 28.3 享元模式的應用
- 28.4 享元模式的擴展
- 28.5 最佳實踐
- 第29章 橋梁模式
- 29.2 橋梁模式的定義
- 29.3 橋梁模式的應用
- 29.4 最佳實踐
- 第三部分 誰的地盤誰做主 ——設計模式PK
- 第30章 創建類模式大PK
- 30.1 工廠方法模式VS建造者模式
- 30.2 抽象工廠模式VS建造者模式
- 第31章 結構類模式大PK
- 31.1 代理模式VS裝飾模式
- 31.2 裝飾模式VS適配器模式
- 第32章 行為類模式大PK
- 32.1 命令模式VS策略模式
- 32.2 策略模式VS狀態模式
- 32.3 觀察者模式VS責任鏈模式
- 第33章 跨戰區PK
- 33.1 策略模式VS橋梁模式
- 33.2 門面模式VS中介者模式
- 33.3 包裝模式群PK
- 第四部分 完美世界 ——設計模式混編
- 第34章 命令模式+責任鏈模式
- 34.2 混編小結
- 第35章 工廠方法模式+策略模式
- 35.2 混編小結
- 第36章 觀察者模式+中介者模式
- 36.2 混編小結
- 第五部分 擴展篇
- 第37章 MVC框架
- 37.2 最佳實踐
- 第38章 新模式
- 38.1 規格模式
- 38.2 對象池模式
- 38.3 雇工模式
- 38.4 黑板模式
- 38.5 空對象模式
- 附錄 23種設計模式彩圖