-
游戲
書中用游戲來引入代理模式,游戲大家都玩過,基本套路就是打怪升級,我們把這段打游戲的過程系統(tǒng)化,非常簡單的一個過程,如圖12-1:

非常簡單,定義一個接口IGamePlayer,表示所有喜愛網絡游戲的玩家,然后定義一個具體的實現(xiàn)類GamePlayer,實現(xiàn)每個游戲愛好者玩游戲要執(zhí)行的功能。定義了三個方法,分別是在網絡游戲中常用的功能:登錄游戲、殺怪和升級,其實現(xiàn)代碼如下:
//玩家接口
public interface IGamePlayer {
void login(String user,String password);
void killBoss();
void upgrade();
}
//玩家實現(xiàn)類
public class GamePlayer implements IGamePlayer {
private String name;
public GamePlayer(String name){
this.name = name;
}
@Override
public void login(String user, String password) {
System.out.println("登入名為"+user+"的用戶"+this.name+"登入成功");
}
@Override
public void killBoss() {
System.out.println(this.name+"擊殺boss");
}
@Override
public void upgrade() {
System.out.println(this.name+"升級");
}
}
//場景類
public class Client {
public static void main(String[] args) {
IGamePlayer player = new GamePlayer("張三");
System.out.println("開始時間是:"+new Date());
player.login("zhangSan","password");
player.killBoss();
player.upgrade();
System.out.println("結束時間是:"+new Date());
}
}
大家玩游戲都知道,升級是一個很痛苦的過程,尤其是一些需要肝到爆的游戲,于是就有了各種外掛,然后被封號;還有一些相關的的職業(yè)誕生了,那就是游戲代練,把賬號交給代練給我們升級,那我們來修改一下類圖12-2所示:

在類圖中增加了一個GamePlayerProxy類來代表游戲代練者,他本質也是一個游戲玩家,因此同樣繼承IGamePlayer接口,代碼如下:
//代練類
public class GamePlayerProxy implements IGamePlayer {
private IGamePlayer gamePlayer;
public GamePlayerProxy(IGamePlayer gamePlayer){
this.gamePlayer = gamePlayer;
}
@Override
public void login(String user, String password) {
this.gamePlayer.login(user,password);
}
@Override
public void killBoss() {
this.gamePlayer.killBoss();
}
@Override
public void upgrade() {
this.gamePlayer.upgrade();
}
}
//場景類
public class Client {
public static void main(String[] args) {
IGamePlayer player = new GamePlayer("張三");
IGamePlayer proxy = new GamePlayerProxy(player);
System.out.println("開始時間是:"+new Date());
proxy.login("zhangSan","password");
proxy.killBoss();
proxy.upgrade();
System.out.println("結束時間是:"+new Date());
}
}
你自己沒有玩游戲,將號交給代練,代練幫你去打怪升級了,這就是代理模式。
-
代理模式的定義
代理模式(Proxy Pattern)是一個使用率非常高的模式,其定義如下:
Provide a surrogate or placeholder for another object to control access to it.(為其他對象提供一種代理以控制對這個對象的訪問)
代理模式的通用類圖如圖12-3:

帶了模式也叫委托模式,它是一項基本設計技巧。許多其他的模式,如狀態(tài)模式、策略模式、訪問者模式本質上是在更特殊的場合采用了委托模式,而且在日常應用中,代理模式可以提供非常好的訪問控制。先看類圖中的三個角色的定義:
- Subject抽象主題角色
抽象主題類可以是抽象類也可以是接口,是一個普通的業(yè)務類型定義,沒有特殊要求,對應例子中的IGamePlayer - RealSubject具體主題角色
也叫做被委托角色、被代理角色。它才是核心業(yè)務邏輯的具體執(zhí)行者,對應GamePlayer - Proxy代理主題角色
也叫作委托類,代理類。它負責對真實角色的應用,把所有抽象主題類定義的方法限制委托給真實主題角色實現(xiàn),并且在真實主題角色處理完畢前后做預處理和善后處理工作。對應GamePlayerProxy
首先,來看一下Subject抽象主題類的通用源碼,代碼如下:
public interface Subject {
//定義一個方法
public void request();
}
在接口中我們定義一個方法request來作為方法的代表,RealSubject對它進行實現(xiàn),代碼如下:
public class RealSubject implements Subject {
@Override
public void request() {
//業(yè)務邏輯處理
}
}
RealSubject是一個正常的業(yè)務實現(xiàn)類,代理模式的和核心就在代理類上,代碼如下:
public class Proxy implements Subject {
//被代理者
private Subject subject = null;
public Proxy(Subject subject){
this.subject = subject;
}
@Override
public void request() {
this.before();
this.subject.request();
this.after();
}
//預處理
private void before(){
//do something
}
//善后處理
private void after(){
//do something
}
}
在代理類中出現(xiàn)了before和after方法,這是一個"引子",能夠引出一個嶄新的編程方式。(我先猜一下是不是指的面向切面編程,spring中有個關于aop的概念,其實現(xiàn)方式就是用的代理模式)
一個代理類可以代理多個被委托者或被代理者,因此一個代理類具體代理哪個真實主題角色,是由場景類決定的。當然,最簡單的情況就是一個主題類和一個代理類,這是最簡潔的代理模式。在通常情況下,一個接口只需要一個代理類就可以了,具體代理哪個實現(xiàn)類由高層模塊代碼來決定,也就是代理類的構造函數(shù)中傳遞被代理者,就如上例中的代理類的構造函數(shù)中傳遞被代理者,你要代理誰就產生該代理的實例,然后把被代理者傳遞進來,該模式在實際的項目應用中比較廣泛。
-
代理模式的應用
3.1 代理模式的優(yōu)點
- 職責清晰
真實的角色就是實現(xiàn)實際的業(yè)務邏輯,不用關心其他非本職責的失誤,通過后期的代理完成一件事物,附帶的結果就是編程簡單清晰。 - 高擴展性
具體主題角色是隨時都會發(fā)生變化,只要它實現(xiàn)了接口,不管它如何變化,都不會違反接口契約限制,那么代理類就完全不需要做任何修改就可以使用。符合開閉原則(發(fā)現(xiàn)基本上好的設計都是符合開閉原則,這是核心的原則) - 智能化
在以上的例子中沒有體現(xiàn)出來,但是在動態(tài)代理的章節(jié)中就可以看到代理的智能化。
3.2 代理模式使用場景
代理模式就是把核心業(yè)務抓在手上自己做,一些外圍的不是很重要的工作交給代理去做,代理模式的使用場景非常多,大家可以看看springAop,這是一個非常典型的動態(tài)代理。
-
代理模式的擴展
4.1 普通代理
在網絡上代理服務器設置分為透明代理和普通代理,是什么意思?透明代理就是用戶不用設置代理服務器地址,就可以直接訪問,也就是說代理服務器對用戶來說是透明的,不用知道它的存在;普通代理則是需要用戶自己設置代理服務器的IP地址,用戶必須知道代理的存在。我們設計模式中的普通代理和強制代理也是一種類似的結構,普通代理就是我們要知道代理的存在,也就是類似的GamePlayerProxy這個類的存在,然后才能訪問;強制代理則是調用者直接調用真實角色,而不用關心代理是否存在,其代理的產生是由真實角色決定的,還是通過例子來看吧。
首先說普通代理,他的要求就是客戶端只能訪問代理角色,而不能訪問真實角色,這個比較簡單,還是以游戲代理為例子,我自己作為一個游戲玩家,練級的任務交給代理,也就是場景類不能再直接new一個GamePlayer對象了,它必須由GamePlayerProxy來進行場景模擬,類圖12-4:

GamePlayer的構造函數(shù)增加了gamePlayer參數(shù),而代理角色則只需要傳入被代理者的名字即可,而不需要說是替哪個對象做代理,代碼如下:
public class GamePlayer implements IGamePlayer {
private String name = null;
public GamePlayer(IGamePlayer gamePlayer,String name) throws Exception {
if(gamePlayer == null){
throw new Exception("不能創(chuàng)建真實角色!");
}else{
this.name = name;
}
}
@Override
public void login(String user, String password) {
System.out.println("登入名為"+user+"的用戶"+this.name+"登入成功");
}
@Override
public void killBoss() {
System.out.println(this.name+"擊殺boss");
}
@Override
public void upgrade() {
System.out.println(this.name+"升級");
}
}
在構造函數(shù)中,傳遞進來一個IGamePlayer對象,檢查誰能創(chuàng)建真實的角色,當然還可以有其他的限制,比如類名必須為proxy,可以根據(jù)實際情況擴展(我看到這里,還是不太明白,這個構造函數(shù)傳IGamePlayer對象干嘛,不太理解這個說法,接著看代碼)
GamePlayerProxy代碼如下:
public class GamePlayerProxy implements IGamePlayer {
private IGamePlayer gamePlayer = null;
public GamePlayerProxy(String name){
try{
this.gamePlayer = new GamePlayer(this,name);
}catch (Exception e){
e.getMessage();
}
}
@Override
public void login(String user, String password) {
this.gamePlayer.login(user,password);
}
@Override
public void killBoss() {
this.gamePlayer.killBoss();
}
@Override
public void upgrade() {
this.gamePlayer.upgrade();
}
}
僅僅修改了構造函數(shù),傳遞進來一個代理者名稱,即可進行代理,在這種改造下,調用者就更簡潔了,調用者只需要知道代理存在就可以了,不用知道代理了誰,場景類代碼如下:
public class Client {
public static void main(String[] args) {
IGamePlayer proxy = new GamePlayerProxy("張三");
System.out.println("開始時間是:"+new Date());
proxy.login("zhangSan","password");
proxy.killBoss();
proxy.upgrade();
System.out.println("結束時間是:"+new Date());
}
}
現(xiàn)在就知道了為什么realSubject的構造函數(shù)傳Subject了,普通代理就必須得知道代理類,可以不知道realSubject,并且創(chuàng)建realSubject是由代理類來創(chuàng)建的,并且無法直接創(chuàng)建realSubject。
在普通代理模式下,調用者只知代理而不用知道真實角色是誰,屏蔽了真實角色的變更對高層模塊的影響,真實的主題角色想怎么修改就怎么修改,對高層模塊沒有任何影響,只要你不違反接口契約就好了,該模式非常適合對擴展性要求高的場景。當然,在實際的項目中,一般都是通過約定來禁止new一個真實的角色(通過約定來限制就不需要通過在構造函數(shù)中傳subject來限制了),這也是一個非常好的方案。
注意 普通代理模式的約束問題,盡量通過團隊的編程規(guī)范約束,畢竟做的越多錯的越多,使用技術約束的方式對系統(tǒng)維護是一種非常不利的因素
4.2 強制代理
強制代理在設計模式中比較另類,為什么這么說呢?一般的思維都是通過代理找到真實角色,但是強制代理卻是要"強制",你必須通過真實角色找到代理角色,否則你不餓能訪問。不管你是通過代理類還是通過直接new一個主題角色類,都不能訪問,只有通過真實角色指定的代理才可以訪問,也就是說由真實角色管理代理角色。這么說吧,高層模塊new了一個真實角色的對象,返回的卻是代理,這就好比你想和一個明星溝通,并且你們是互相認識的,于是你撥通了他的電話,他說現(xiàn)在比較忙,你可以先和他經濟人溝通;你想繞過代理,誰知道返回的還是他的代理,這就是強制代理,你可以不用知道代理的存在(這個剛好和普通代理是相反的),但是你的所作所為還是代理給你提供的,強制代理類圖12-5如下:

在接口上增加一個getProxy方法,真實角色GamePlayer可以指定一個自己的代理,除了代理誰都不能訪問。接口代碼如下:
public interface IGamePlayer {
void login(String user, String password);
void killBoss();
void upgrade();
IGamePlayer getProxy();
}
僅僅增加一個getProxy方法,指定要訪問自己必須通過哪個代理,實現(xiàn)類GamePlayer代碼如下:
public class GamePlayer implements IGamePlayer {
private String name = null;
private IGamePlayer proxy = null;
public GamePlayer(String name) {
this.name = name;
}
@Override
public void login(String user, String password) {
if(this.isProxy()){
System.out.println("登入名為"+user+"的用戶"+this.name+"登入成功");
}else{
System.out.println("請使用指定代理訪問");
}
}
private boolean isProxy() {
if(this.proxy == null){
return false;
}
return true;
}
@Override
public void killBoss() {
if(this.isProxy()){
System.out.println(this.name+"擊殺boss");
}else{
System.out.println("請使用指定代理訪問");
}
}
@Override
public void upgrade() {
if(this.isProxy()){
System.out.println(this.name+"升級");
}else{
System.out.println("請使用指定代理訪問");
}
}
@Override
public IGamePlayer getProxy() {
if(this.proxy == null){
this.proxy = new GamePlayerProxy(this);
}
return this.proxy;
}
}
增加了一個私有方法,檢查是否是自己制定的代理,是指定的代理則允許訪問,否則不許訪問。在看看代理角色,代碼如下:
public class GamePlayerProxy implements IGamePlayer{
private IGamePlayer gamePlayer;
public GamePlayerProxy(IGamePlayer gamePlayer){
this.gamePlayer = gamePlayer;
}
@Override
public void login(String user,String password){
this.gamePlayer.login(user,password);
}
@Override
public void killBoss(){
this.gamePlayer.killBoss();
}
@Override
public void upgrade(){
this.gamePlayer.upgrade();
}
@Override
public IGamePlayer getProxy() {
return this;
}
}
代理角色可以再次被代理,這里就沒有繼續(xù)延伸下去了,查找代理的方法就返回自己的實例。
public class Client {
public static void main(String[] args) {
//正常邏輯訪問
IGamePlayer player= new GamePlayer("張三");
System.out.println("開始時間是:"+new Date());
player.login("zhangSan","password");
player.killBoss();
player.upgrade();
System.out.println("結束時間是:"+new Date());
//任意代理訪問
IGamePlayer proxy = new GamePlayerProxy("張三");
System.out.println("開始時間是:"+new Date());
proxy.login("zhangSan","password");
proxy.killBoss();
proxy.upgrade();
System.out.println("結束時間是:"+new Date());
//指定代理訪問
IGamePlayer proxy = player.getProxy();
System.out.println("開始時間是:"+new Date());
proxy.login("zhangSan","password");
proxy.killBoss();
proxy.upgrade();
player.login("zhangSan","password");
player.killBoss();
player.upgrade();
System.out.println("結束時間是:"+new Date());
}
}
這里有三種場景,一是正常的自己調用方法,返回沒有指定代理;二是隨便創(chuàng)建一個代理,也無法使用;三是通過玩家指定的代理才能正常使用,(*這里玩家指定了代理后,玩家自己也可以調用方法了,這個例子的邏輯并不很嚴謹,可能是通過團隊開發(fā)規(guī)則約束吧
)
強制代理的概念就是要從真實角色查找到代理角色,不允許直接訪問真實角色。高層模塊只要調用getProxy就可以訪問真實角色的所有方法,他根本不需要產生一個代理出來,代理的管理已經由真實角色自己完成了。(這個得在實際開發(fā)中加深理解,目前不是太明白這樣設計的用意,雖然這個例子只能通過被代理類指定代理,但是指定完代理后自己也可以完成業(yè)務,可以不通過代理完成業(yè)務)
4.3 代理也是有個性的
一個類可以實現(xiàn)多個接口,完成不同任務的整合。也就是說代理類不僅僅可以實現(xiàn)主題接口,也可以實現(xiàn)其他接口完成不同的任務,而且代理的目的是在目標對象的基礎上作增強,這種增強的本質通常是對目標對象的方法進行攔截和過濾。例如游戲代理是需要收費的,升一級需要5元錢,這個計算功能就是代理類的個性,它應該在代理接口中定義,類圖12-6:

增加一個IProxy接口,其作用是計算代理的費用。我們先來看IProxy接口,代碼如下:
public interface IProxy{
public void count();
}
代理接口僅一個方法,再看看代理類的變化,代碼如下:
public class GamePlayerProxy implements IGamePlayer,IProxy{
private IGamePlayer gamePlayer = null;
public GamePlayerProxy(IGamePlayer gamePlayer){
this.gamePlayer = gamePlayer;
}
@Override
public void login(String user,String password){
this.gamePlayer.login(user,password);
}
@Override
public void killBoss(){
this.gamePlayer.killBoss();
}
@Override
public void upgrade(){
this.gamePlayer.upgrade();
this.count();
}
@Override
public void count(){
System.out.println("升級費用是:150元");
}
}
實現(xiàn)IProxy接口,同時在升級方法中調用該方法,其他類都沒有改動。
代理類不僅僅是可以有自己的運算方法,通常情況下代理類的職責并不一定單一,他可以組合其他真實角色,也可以實現(xiàn)自己的職責,比如計算費用。代理類可以為真實角色預處理消息、過濾消息、消息轉發(fā)、事后處理消息等功能。當然一個代理類,可以代理多個真實角色,并且真實角色之間可以有耦合關系。(這個最好的例子就spring中aop相關的知識)
4.4動態(tài)代理
上面的章節(jié)都是引子,動態(tài)代理才是重頭戲。什么是動態(tài)代理?動態(tài)代理在實現(xiàn)階段不用關心代理誰,而在運行階段才指定代理哪一個對象。相對來說,自己寫代理類的方式是靜態(tài)代理。本章節(jié)的核心部分就在動態(tài)代理上,就是我在前面一直有提到的AOP(Aspect Oriented Programming),其核心就是采用了動態(tài)代理機制,一起學習一下動態(tài)代理是怎么實現(xiàn)的,還是以打游戲為例,類圖修改一下以實現(xiàn)動態(tài)代理,類圖12-7:

在類圖中增加了一個InvocationHandler接口和GamePlayIH類,作用就是產生一個對象的代理對象,其中InvocationHandler是JDK提供的動態(tài)代理接口,對被代理的方法進行代理。我們看程序,接口保持不變,實現(xiàn)類也沒有變化,來看看DynamicProxy類,代碼如下:
public class GamePlayerIH implements InvocationHandler {
//被代理者
Class cls = null;
//被代理的實例
Object obj = null;
//我要代理誰
public GamePlayerIH(Object obj){
this.obj = obj;
}
//調用別代理的方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(this.obj,args);
return result;
}
}
其中invoke方法是接口InvocationHandler定義必須實現(xiàn)的,它完成對真實方法的調用。我們來詳細學習一下InvocationHandler接口,動態(tài)代理是根據(jù)被代理的接口生成所有的方法,也就是說給定一個接口,動態(tài)代理會宣稱"我已經實現(xiàn)該接口下的所有方法了",那動態(tài)代理怎么才能實現(xiàn)被代理的接口中的方法呢?默認情況下所有方法的返回值都是空的,代理已經實現(xiàn)它了,但是沒有任何的邏輯含義,那怎么辦?通過InvocationHandler接口,所有的方法都由該Handler來進行處理,即所有被代理的方法都由InvocationHandler接管實際的處理任務。(本人也是一臉蒙蔽,也就猜到可能會通過反射去實現(xiàn)接口的方法,接著往下看看,看是怎么處理的)
接下來看場景類,代碼如下:
public class Client {
public static void main(String[] args) {
IGamePlayer player = new GamePlayer("zhangsan");
InvocationHandler handler = new GamePlayerIH(player);
System.out.println("開始時間是:"+new Date());
//獲得類的class loader
ClassLoader cl = player.getClass().getClassLoader();
//動態(tài)產生一個代理者
IGamePlayer proxy = (IGamePlayer) Proxy.newProxyInstance(cl,new Class<?>[]{IGamePlayer.class},handler);
proxy.login("zhangsan","123456");
proxy.killBoss();
proxy.upgrade();
System.out.println("結束時間是:"+new Date());
}
}
我們還是讓代練者幫我們打游戲,但是我們既沒有創(chuàng)建代理類,也沒有實現(xiàn)IGamePlayer接口,這就是動態(tài)代理。(我到這里很蒙蔽,比不明白其中實現(xiàn)的原理,但是很神奇,這就是代碼的魅力)動態(tài)代理可不僅僅就這么多類容,還有更重要的,如果想讓游戲登入后發(fā)一個信息給我們,防止賬號被人盜用,該怎么處理?直接修改被代理類GamePlayer的邏輯?這不是一個好辦法,解決辦法如下代碼:
public class GamePlayerIH implements InvocationHandler {
//被代理者
Class cls = null;
//被代理的實例
Object obj = null;
//我要代理誰
public GamePlayerIH(Object obj){
this.obj = obj;
}
//調用被代理的方法
@Override
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;
}
}
我們只是在代理中增加一個判斷調用的方法是否是login就可以決定是否要發(fā)送信息;有人用我的賬號就發(fā)送一個信息,然后看看自己的賬號是不是被人盜了,非常好的方法,這就是AOP編程。AOP編程沒有使用什么新的技術,但是它對我們的設計、編碼有非常大的影響,對于日志、事物、權限等都可以在系統(tǒng)設計階段不用考慮,而在設計后通過AOP的方式切過去。我們來看看動態(tài)代理模型,類圖12-8:

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

其實我們以上代碼還能進一步的擴展,看DynamicProxy類,它是一個通用類,不具有業(yè)務意義,如果我們在產生一個實現(xiàn)類是不是就很有意義了呢?代碼如下:
public class SubjectDynamicProxy extends DynamicProxy {
public static <T> T newProxyInstance(Subject subject){
//獲得ClassLoader
ClassLoader loader = subject.getClass().getClassLoader();
//獲得接口數(shù)組
Class<?>[] classes = subject.getClass().getInterfaces();
//獲得handler
InvocationHandler handler = new MyInvocationhandler(subject);
return newProxyInstance(loader,classes,handler);
}
}
如此擴展以后,高層模塊對代理的訪問就跟簡單了,代碼如下:
public class Client {
public static void main(String[] args) {
Subject subject = new RealSubject();
Subject proxy = SubjectDynamicProxy.newProxyInstance(subject);
proxy.doSomething("Finish");
}
}
是不是更簡單了,那么問題來了,靜態(tài)代理好像也是這么個寫法,一個主題類,在創(chuàng)建一個代理類,然后通過代理類去執(zhí)行業(yè)務。這是有區(qū)別的,注意看父類,動態(tài)代理的主要意圖就是解決我們常說的"審計"問題,也就是面向切面編程,在不改變我們已有代碼結構的情況下增強或控制對象的行為。
注意
要實現(xiàn)動態(tài)代理的首要條件是:被代理類必須實現(xiàn)一個接口,回想一下前面的分析(代碼中有一步不就是獲取被代理類的所有接口嗎)。當然了,現(xiàn)在也有很多技術如CGLIB可以實現(xiàn)不需要接口也可以實現(xiàn)的動態(tài)代理的方式 。
再次說明,以上的動態(tài)代理是一個通用代理框架。如果你想設計自己的AOP框架,完全可以在此基礎上擴展,我們設計的是一個通用代理,只要有一個接口,一個實現(xiàn)類,就可以使用該代理,完成代理的所有功效
-
最佳實踐
代理模式應用的非常廣泛,達到一個系統(tǒng)框架、企業(yè)平臺,小到代碼片段、事物處理,稍不留意就用到代理模式了。這可能是大家接觸最多的模式,而且有了AOP大家寫代理就更加簡單了,有類似SpringAop和AspectJ這樣非常優(yōu)秀的工具,拿來主義即可!不過,大家可以看看源代碼,特別是調試的時候,只要看到類似$Proxy()這樣的結構,就應該知道這是一個動態(tài)代理了。
在學習AOP框架的時候,弄清楚幾個名詞就成:切面(Aspect)、切入點(JoinPoint)、通知(Advice)、織入(Weave)就夠了,理解這些,應用時就游刃有余了!
上面有提到了動態(tài)代理模式的實現(xiàn)方式,jdk是基于接口實現(xiàn)的也就是例子中的實現(xiàn)方式,我們可以看出,是通過獲取所有的接口,然后通過反射執(zhí)行接口中對應的方法來實現(xiàn)的;我去百度了一下cglib動態(tài)代理(基于繼承),java是單繼承的,那么用cglib的方式就限制比較多了,只能代理一個類的;另外,不管是接口還是繼承,都只能代理接口或父類中的方法,被代理類自己的方法是代理無法處理的
內容來自《設計模式之禪》