代理模式

版權聲明:本文爲博主原創文章,轉載請註明出處,歡迎交流學習!java

      有這樣一種場景,有一個實現類實現了某種功能,這個實現類咱們沒法修改或者不容許被修改,可是除了實現類裏的功能咱們還須要擴展其餘的功能,這種狀況時咱們該怎麼辦呢?咱們能夠建立一個代理類,在代理類裏調用實現類裏的功能而且在代理類中擴展咱們須要的功能,客戶端直接調用代理類而不須要關心實現類,這就是代理模式的思想。簡單來講,代理模式的本質就是建立一個代理,代理類在原有類的行爲基礎上再加上一個額外的行爲,甚至是替換掉原有類的行爲,返回一個代理供客戶端調用。spring

      舉一個生活中常見的例子,咱們平時去房產中介公司租房,房產中介提供出租房子的功能,可是房產中介自己並無房子,房子是房東委託給中介公司,受權給他出租,租房者不須要關心房東的信息或者說沒法得到房東的信息,只須要直接向房產中介獲取租房信息。可能這個例子不是很恰當,可是它反映了代理模式的思想。房東具備出租房屋的功能,房東直接跟中介交互並把這種功能委託給中介,所以中介具備了出租房屋的功能,而且額外加入收取中介費的功能,租房者直接跟中介交互經過中介得到房東出租房屋的功能。數組

      代理模式中有如下幾個角色:框架

      1)抽象角色:聲明真實對象和代理對象的共同接口;學習

      2)代理角色:代理角色內部有對真實角色的引用,所以能夠操做真實對象的功能;代理對象和真實對象實現相同的接口,以便在任什麼時候候均可以代替真實對象;代理對象能夠在執行真實對象的操做時,加上一些額外的操做,至關於對真實對象進行封裝;this

      3)真實角色:代理角色所表明的對象,是咱們最終要操做的對象。spa

      在上面的舉例中,抽象角色就是出租房屋這一功能,房東就至關於真實角色,實現了抽象角色這一接口,具備了出租房屋的功能,房產中介就至關於代理角色,他引用了真實角色出租房屋的功能,而且額外加上了收取中介費的功能。代理

      代理模式能夠分爲兩種:靜態代理模式動態代理模式。這兩種代理模式本質上是同樣的,只是生成代理類的方式不同。code

      一、靜態代理模式對象

      靜態代理模式採用的方式是手動引入真實角色的引用,這種方式將被代理的對象定義死了,所以靜態代理適合被代理對象很固定、只須要去代理一個類或者若干個固定的類而且數量不是不少的場景。代碼示例:

      定義抽象角色:      

1 /**
2  * 抽象角色:
3  * 定義一個接口或者虛擬類
4  * 
5  */
6 public interface Subject {
7     
8       public void rentHouse();
9 }

     

      定義真實角色:     

 1 /**
 2  * 真實角色:
 3  * 實現抽象角色接口
 4  */
 5 public class RealSubject implements Subject {
 6 
 7     public void rentHouse() {
 8         
 9         System.out.println("房東:出租房屋");
10     }
11 
12 }

 

      定義代理角色:      

 1 /**
 2  * 代理角色:
 3  * 一、實現抽象角色接口;
 4  * 二、維護一個真實對象的引用,用來操做真實對象;
 5  * 三、額外添加功能;
 6  *
 7  */
 8 
 9 public class ProxySubject implements Subject{
10     
11     private RealSubject realSubject; //代理角色內部引用了真實角色
12 
13     public void rentHouse() {
14         
15         if(null == realSubject){
16             realSubject = new RealSubject();
17         }
18         
19         realSubject.rentHouse();//執行真實角色所完成的事情
20         
21         this.getFee();//額外加入本身的功能
22         
23     }
24     
25     private void getFee(){
26         System.out.println("代理角色:收取中介費");
27     }
28     
29 }

 

      客戶端調用:    

 1 public class Client {
 2     
 3     public static void main(String[] args) {
 4         
 5         Subject subject = new ProxySubject();//得到代理對象,由代理對象來執行所需的操做
 6         
 7         subject.rentHouse();//輸出結果:房東:出租房屋
 8                             //         代理角色:收取中介費
 9         
10     }
11 
12 }

       靜態代理在使用場景上有必定的侷限性,由於代理類中引入的對象是寫死的(好比上面的RealSubject),所以它只能代理固定的一個類或若干個類。當須要代理一系列類中的某些方法,好比比較典型的應用就是springAOP,咱們須要建立出一批代理類來代理一系列類中的某些方法,這個時候靜態代理就很難知足咱們的需求了,咱們須要用動態代理的方式。

 

      二、動態代理

      動態代理是JDK自帶的功能,它涉及到一個接口InvocationHandler和一個類Proxy,這兩個類都在java.lang.reflect包下。咱們須要實現InvocationHandler接口,而且調用Proxy類的靜態方法newProxyInstance方法來得到一個代理類的實例。動態代理的實現過程與靜態代理最大的區別就是代理實例是動態生成的。咱們先來看看實現過程:

      定義抽象角色:      

1 /**
2  * 抽象角色:
3  * 定義一個接口
4  * 
5  */
6 public interface Subject {
7     
8       public void rentHouse();
9 }

 

      定義真實角色:   

 1 /**
 2  * 真實角色:
 3  * 實現抽象角色接口
 4  */
 5 public class RealSubject implements Subject {
 6 
 7     public void rentHouse() {
 8         
 9         System.out.println("房東:出租房屋");
10     }
11 
12 }

 

      定義動態代理角色:     

 1 /**
 2  * 動態代理角色:
 3  * 
 4  * 該代理類的內部屬性是Object類型,實際使用的時候經過該類的構造方法傳入一個對象,
 5  * 此外,該類還實現了invoke方法,該方法中的method.invoke其實就是調用被代理對象
 6  * 的將要被執行的方法,參數sub表示執行的方法從屬於sub,在動態代理類中咱們還能夠
 7  * 在執行真實對象的方法以外再額外加入一些本身的方法
 8  *
 9  */
10 public class DynamicProxySubject implements InvocationHandler {
11     
12     //被代理類對象,聲明爲Object類型,這樣就能夠傳入任何類型的對象
13     private Object sub; 
14     
15     public DynamicProxySubject(Object obj){
16         this.sub = obj;
17     }
18 
19     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
20 
21         System.out.println("before calling :"+ method); //能夠在執行真實對象的方法以外加入本身額外的方法
22         
23         method.invoke(sub, args);//反射機制
24         
25         System.out.println("after calling :"+ method);
26         return null;
27     }
28 
29 }

 

      客戶端調用:    

 1 public class Client {
 2     
 3     public static void main(String[] args) {
 4         //建立被代理類的對象,根據傳入的這個對象得到調用處理程序(handler)
 5         RealSubject realSubject = new RealSubject();
 6         
 7         //建立一個調用處理程序(handler),因爲DynamicProxySubject類實現了InvocationHandler接口,
 8         //所以,handler能夠指向DynamicProxySubject實例,在後面生成代理實例時須要傳入這個handler
 9         InvocationHandler handler = new DynamicProxySubject(realSubject);
10         
11         Class<?> classType = handler.getClass();
12         
13         //一次性生成代理類的實例
14         Subject subject = (Subject)Proxy.newProxyInstance(classType.getClassLoader(), 
15                           realSubject.getClass().getInterfaces(), handler);
16         
17         //代理實例調用方法時,將其指派到它的調用處理程序(handler)的invoke方法,流程轉入invoke方法的調用
18         subject.rentHouse();
19         
20         //打印動態生成的代理實例的Class對象,能夠看出生成的代理類的類型
21         System.out.println(subject.getClass());
22         
23     }
24 
25 }

      以上代碼是動態代理模式的實現過程的示例,與靜態代理的實現過程不一樣,代理類須要實現InvocationHandler這個接口,此接口只有一個invoke方法,每個代理實例都具備一個關聯的調用處理程序(handler),對代理實例調用方法時,將會被與之關聯的handler接管,轉而調用handler的invoke方法。invoke方法的參數中,method參數表示代理實例調用的那個方法對應的Method對象,好比上面的程序裏就表示rentHouse()方法的Method對象;args參數表示被調用方法的參數列表。

      Proxy類的靜態方法newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)會動態返回一個代理類的實例,第一個參數表示代理類的類加載器;咱們須要注意第二個參數,它是一個接口數組,表示代理類要實現的接口列表,也就是說動態生成的這個代理實例實現了這些接口;第三個參數表示與代理實例關聯的調用處理程序(handler)。

      運行程序,咱們能夠獲得如下結果輸出:

      

      從運行結果能夠看出,第一行和第三行信息是代理類中咱們加入的額外的方法,第二行是代理類代理執行RealSubject類中的方法。咱們來看看第四行信息,它就是咱們打印的動態生成的代理類,這個類是運行期間動態生成的一個類,它既不是RealSubject類型也不是DynamicProxySubject類型,它是$Proxy0類型的。在代碼中,因爲咱們傳入的接口數組是realSubject對象所實現的接口,即Subject接口,所以生成的這個代理類也實現了Subject接口,根據多態,咱們能夠將生成的代理類強制轉換成Subject類型。

      動態代理模式適合被代理對象類型不肯定的場景,咱們在代理類DynamicProxySubject中把被代理對象定義爲Object類型,這樣就能夠代理全部類型的對象,在上面的例子中,咱們傳入的被代理對象是RealSubject類型。代理類是在運行時根據咱們傳入的被代理對象以及須要實現的接口等信息動態生成的,它的類型是不固定的(可是都是以$Proxy開頭)。

      以上內容給你們介紹了代理模式的靜態代理和動態代理原理,咱們要根據不一樣的應用場景來選擇用哪種,其實最重要的是要理解這種思想,在Spring框架底層源碼中不少地方也用到了代理模式,理解了代理模式對咱們研究框架頗有幫助。以上只是LZ我的的看法,僅供參考。

相關文章
相關標籤/搜索