這次寫代理模式,代理模式裡面的動態代理(Dynamic Proxy)邏輯稍顯混亂,不太容易理解,本章節會將代理模式裡面的靜態代理(Static Proxy)和動態代理(Dynamic Proxy)結合對比理解,其中動態代理的實現需要具備 Java 的反射(Reflection)知識,原理方面會結合實例和靜態代理的對比盡量說清楚。
在某些情況下,一個客戶不想或者不能直接引用一個對象,代理對象就再客戶端和被代理對象之間起到中介的作用。就好比你在北京租房,初來乍到,人生地不熟,找房子遍地都是中介,想找房東可沒那麼容易(基本算得上是找不到房東直租的)。問題來了,找不到房東直租,但房子又是一切的基礎,so....走中介,房東能租房,中介也能租房,不過是你通過中介去將房東的房子租給自己。OK,這就是一個活生生的代理模式的例子,相必在外漂泊的小年輕們都感同身受吧。
再說說代理模式的分類,代理模式主要分為兩類:靜態代理(Static Proxy)、動態代理(Dynamic Proxy)。
靜態代理:一個被代理的真實對象對應一個代理,相當於一個租房中介只代理一個房東。弊端很明顯,這樣下去中介早餓死了!!!並且中介公司管理這麼龐大的中介團隊早晚逗得垮掉。反應到代碼裡面就是:代理類急劇增加,靈活性降低,增加了代碼的復雜度。
動態代理:動態代理主要是去解決靜態代理存在的問題(及一個代理對應一個被代理對象),現實中也是這樣,不可能有中介只做一個房東的生意,一個中介手裡n多房子供你選擇,3人間、6人間、隔斷、小兩居等各種房源。反應到代碼裡面:代理類只有一個,根據客戶需求動態的去改變代理的真實對象,增加了代碼的靈活性,降低了代碼的復雜性。
代理模式的目的在於:為其他對象提供一種代理的方式控制被代理對象。
代理模式主要涉及到三個角色:抽象角色、代理角色、真實角色(被代理的角色)。
抽象角色:聲明真實對象和代理對象的共同接口。即真實對象和代理對象共同要實現的行為動作(好比房東和中介都要能夠實現租房的行為,都能把房子租給你)。
代理角色:代理角色內部含有對真實角色的引用,從而可以去操作真實對象,同時代理對象提供與真實對象的接口,以便在任何時候都能代替真實對象。同時,代理對象在執行真實對象的操作時,也能附加它自己的操作,相當於對真實對象的封裝(可以理解為中介在執行將房東的房子租給你這一操作時,可以向你收取中介費,還可以在退房的時候扣你押金這類房東不具有的操作)。
真實角色:代理角色所代理的對象,亦即我們最終要引用的對象。
靜態代理主要涉及到的三個角色關系圖如下:
代理角色和真實角色都要實現同一個操作行為接口(或繼承同一個抽象類),並且代理角色依賴於真實角色,因為代理角色內部有對真實角色的引用,代理在操作真實角色去執行動作時,必須要知道是哪 個真實角色。
下面是靜態代理的實現:
聲明抽象角色和代理角色實現的共同接口。
1 package com.cnblogs.vincentzh.staticproxy; 2 //聲明出租房屋接口 3 public interface RentOut 4 { 5 public void rentOut(); 6 }
創建真實角色,實現租房接口。
1 package com.cnblogs.vincentzh.staticproxy; 2 // 創建房主類(真實角色) 3 public class HouseHolder implements RentOut 4 { 5 @Override 6 public void rent() 7 { 8 System.out.println("I’m renting the house!"); 9 } 10 }
創建代理角色,實現與真實角色相同的接口
1 package com.cnblogs.vincentzh.staticproxy; 2 // 創建代理(中介)角色,與房東實現相同接口 3 public class Proxy implements RentOut 4 { 5 private HouseHolder houseHolder; // 代理角色內部含有真實角色的引用 6 7 // 重寫租房方法,添加中介操作 8 @Override 9 public void rent() 10 { 11 this.preRentOut(); // 代理對象添加自己的操作 12 13 if(null == houseHolder) 14 { 15 houseHolder = new HouseHolder(); 16 } 17 houseHolder.rent(); 18 19 this.postRentOut(); // 代理對象添加自己的操作 20 } 21 22 // 中介操作,租房前收中介費 23 public void preRentOut() 24 { 25 System.out.println("I need more money!"); 26 } 27 28 // 中介操作,租房後扣押金 29 public void postRentOut() 30 { 31 System.out.println("I will deduct some money!"); 32 } 33 }
創建真實環境類,實現代理功能。
客戶對象並不知道被代理的真實對象,只知道自己需要進行某項操作,並且某個代理能幫助自己完成這一操作。(即客戶要租房子,並不知道要從哪個房東手裡租房,只知道中介能幫助自己租到房子,至於房子從哪個房東那裡租的,就是中介(代理)的事兒了,客戶並不知道,也不需要直到)。
1 package com.cnblogs.vincentzh.staticproxy; 2 // 租房客戶類 3 public class Client 4 { 5 /** 6 * @param args 7 */ 8 public static void main(String[] args) 9 { 10 // 涉世未深的小年輕要租房,苦於聯系不到房東,只能通過中介 11 RentOut rentOut = new Proxy(); 12 rentOut.rent(); 13 } 14 }
運行程序,客戶類通過代理訪問真實角色,不僅實現真實角色的操作行為,同時也添加了代理自己的操作行為。
靜態代理的弊端就在於:每當有一個真實對象需要被代理,就需要創建一個新的代理類,因為代理類內含有對真實對象的引用,代理類需要與真實對象一一對應。這樣,當需要代理的真實對象比較多時,代理類就會急劇增加,增加了代碼的復雜性,使代碼變得尤其繁重,不易維護。那麼動態代理就很好的解決了這一問題。
動態代理解決了靜態代理存在的弊端,實現了多個被代理真實對象僅由一個代理類代理即可。
1)創建兩個行為接口,分別實現 rent、sale 不同操作的行為;
1 package com.cnblogs.vincentzh.dynamicproxy; 2 // 創建租房接口 3 public interface Rent 4 { 5 public void rent(); 6 }
1 package com.cnblogs.vincentzh.dynamicproxy; 2 // 創建售賣接口 3 public interface Sale 4 { 5 public void sale(); 6 }
2)創建兩個需要被代理的真實對象類,且他們分別實現了含有不同操作行為的接口;
1 package com.cnblogs.vincentzh.dynamicproxy; 2 // 創建房主類(真實角色),實現租房接口 3 public class HouseHolder implements Rent 4 { 5 @Override 6 public void rent() 7 { 8 System.out.println("HouseHolder: I’m renting the house!"); 9 } 10 }
1 package com.cnblogs.vincentzh.dynamicproxy; 2 // 創建商人類(真實對象),實現售賣接口 3 public class BusinessMan implements Sale 4 { 5 @Override 6 public void sale() 7 { 8 System.out.println("BusinessMan: I'm salling something!"); 9 } 10 }
3)創建動態代理類,只需要一個代理類,就能夠實現對 n 個需代理對象的代理,從而解決了靜態代理中代理與真實對象一一對應導致類的數目急劇增加的問題。
1 package com.cnblogs.vincentzh.dynamicproxy; 2 // 創建動態代理類,實現代碼運行過程中對各種真實對象的動態代理 3 import java.lang.reflect.InvocationHandler; 4 import java.lang.reflect.Method; 5 6 public class DynamicProxy implements InvocationHandler 7 { 8 private Object realSubject; 9 10 public DynamicProxy(Object realSubject) 11 { 12 this.realSubject = realSubject; 13 } 14 15 public void setRealSubject(Object realSubject) 16 { 17 this.realSubject = realSubject; 18 } 19 20 21 @Override 22 // 實現InvocationHandler接口的 invoke 方法,當代理類調用真實對象的方法時, 23 // 將直接尋找執行 invoke 方法。 24 public Object invoke(Object proxy, Method method, Object[] args) 25 throws Throwable 26 { 27 this.preRent(); // 執行代理自己添加的行為操作 28 29 method.invoke(realSubject, args); // 以反射(reflection)的形式引用真實對象的方法 30 31 this.postRent(); // 執行代理自己添加的行為操作 32 return null; 33 } 34 35 // 代理類自行添加的行為 36 public void preRent() 37 { 38 System.out.println("I need more money!"); 39 } 40 41 // 代理類自行添加的行為 42 public void postRent() 43 { 44 System.out.println("I will deduct some money!"); 45 } 46 }
4)客戶調用類,只需通過改變被代理的真實對象,就能直接實現被代理的對象的更換,就不需要再去為 BusinessMan 類創建一個新的代理類了。BusinessMan類和HouseHolder類公用一個DynamicProxy動態代理類,但在做代理時,卻是動態生成兩個不同的代理實例(Proxy Instance),這就是所謂的動態代理。
1 package com.cnblogs.vincentzh.dynamicproxy; 2 //創建客戶類 3 import java.lang.reflect.Proxy; 4 5 public class Client 6 { 7 /** 8 * @param args 9 */ 10 public static void main(String[] args) 11 { 12 HouseHolder houseHolder = new HouseHolder(); 13 DynamicProxy handler = new DynamicProxy(houseHolder); // 生成 HouseHolder 的代理 14 15 // 動態生成代理實例(HouseHold代理實例),代理支持的接口由初始化參數(第二個)指定,代理實例處理操作所調用的 handler 由第三個參數指定 16 Rent rent = (Rent)Proxy.newProxyInstance(HouseHolder.class.getClassLoader(), HouseHolder.class.getInterfaces(), handler); 17 rent.rent(); // 執行客戶需要進行的行為操作,動態生成的代理實例直接調用指定 handler 的 invoke 方法 18 19 System.out.println("----------------------------------"); 20 21 BusinessMan businessMan = new BusinessMan(); 22 handler.setRealSubject(businessMan); // 為代理更換引用的真實對象(即原本被代理的 HouseHolder 被更換為了 BusinessMan) 23 // 動態生成代理實例(BusinessMan代理實例) 24 // 注:代理實例實在代碼執行過程中動態執行的 25 Sale sale = (Sale)Proxy.newProxyInstance(BusinessMan.class.getClassLoader(), BusinessMan.class.getInterfaces(), handler); 26 sale.sale(); 27 } 28 }
執行結果:
代理模式解決了不能直接操作真實對象的問題,其中的動態代理更是使代理模式的使用簡化不少。若使用靜態代理,那麼當有不用的真實對象(分別實現了不同的操作接口),我們只能去為被代理的真實對象重新生成一個代理類,而動態代理就解決了這一問題,用一個代理類,解決了所有真實對象的代理。通俗點將,這是個全能的代理,既能干租房中介的活,還能干買菜中介的活,也能干媒人的活,聽起來貌似很牛逼的樣子。