插件化開發筆記(一)代理模式

前言

      插件化開發所涉及到的技術點非常多,比如程序的啓動流程、四大組件啓動流程、ClassLoader原理、上下文Context、AMS原理、反射、代理等。本篇主要簡單介紹代理模式(實際上只是一篇學習筆記),爲後面介紹插件化實現做知識鋪墊。

一、定義

       定義:爲其他對象提供一種代理,以控制對這個對象的訪問,這種形式稱爲代理模式。(看起來挺抽象的,不好理解,能理解下面的解釋就夠了)

       代理模式也叫委託模式,是結構性設計模式的一種。在現實生活中,我們用到類似代理模式的場景非常多,比如買房者找房屋中介買房、受害者委託律師打官司、老闆安排助理採購等。這些場景有一個共同特點:真正想做某件事的人 ,因爲行業壁壘等各種原因自己不容易完成,於是轉而找其他專業人士來替自己完成這個意願。以老闆安排助理採購電腦設備爲例:老闆(即委託者)是真是需要購買電腦設備的人,但由於缺乏對電腦設備的瞭解,於是委派助理(即代理)來完成自己的這次採購意願。

 

二、角色及結構

       在代理模式中,包含了如下的角色:

       Subject:抽象主題類,聲明真實主題與代理的共同接口方法。也就是定義一個接口,定義老闆和助理的這次行爲:採購!

       RealSubject:真實主題類,定義了代理所表示的集體對象,客戶端通過代理類間接調用真實主題類的方法。即老闆,真正需要採購的人。

       Proxy:代理類,持有對真實主題類的引用,在其所實現的接口方法中調用真實主題類中相應的接口方法執行。即助理,以老闆的名義去採購。

       Client:客戶端類。也就是一段邏輯代碼,將上述角色組織起來,完成這次採購行爲。

       類結構圖如下:

 

三、代碼實現

       從編碼的角度來說,代理模式分爲靜態代理和動態代理,可以結合下面的例子來理解。(1)靜態代理,在代碼運行前代理類的class文件就已經存在了,結合下面的代碼直觀的理解就是,代理類Assistant類是寫死的,在運行前這個編譯類Assistant.class就存在了。(2)動態代理,則是在代碼運行時纔會通過反射來動態地生成代理類對象,並確定到底來代理誰。也就是說,我們在編碼階段並不會定義出這個代理類,而是在運行的時候動態生成。從下面的代碼實現中,我們發現,動態代理實現時,並沒有出現Assistant這個類。

       下面我們來用代碼實現靜態代理和動態代理。

    1、靜態代理

 1 //Subject:抽象主題類,定義了老闆和助理的這次行爲
 2 public interface IShop {
 3     void buy();
 4 }
 5 
 6 //RealSubject:真實主體類。實現了抽象主題接口
 7 public class Boss implements IShop {
 8     @Override
 9     public void buy() {
10         System.out.println("I am boss,I buy buy buy");
11     }
12 }
13 
14 //Proxy:代理類。以組合的方式持有了對真實主題類Boss的引用,也實現了抽象主題接口。在實現的buy方法中,調用了真實主題Boss的對應方法。
15 public class Assistant implements IShop {
16     private IShop mBoss;
17     Assistant(IShop shoper) {
18         mBoss = shoper;
19     }
20 
21     @Override
22     public void buy() {
23         mBoss.buy();
24     }
25 }
26 
27 //Client:客戶端類
28 public class ProxyDemo {
29     public static void main(String[] args) {
30         IShop boss = new Boss();
31         IShop assitant = new Assistant(boss);
32         assitant.buy();
33     }
34 }

 運行結果

I am boss,I buy buy buy

       這裏我們可以看到,Client類中調用的是代理Assistant的buy方法,而實際完成的是委託者Boss的buy方法,從而實現了這次代理行爲。       

    2、動態代理

 1 public interface IShop {
 2     void buy();
 3 }
 4 
 5 public class Boss implements IShop {
 6     @Override
 7     public void buy() {
 8         System.out.println("I am boss,I buy buy buy");
 9     }
10 }
11 
12 //Java提供了動態的代理接口InvocationHandler,實現該接口需要重寫invoke方法。
13 import java.lang.reflect.InvocationHandler;
14 import java.lang.reflect.Method;
15 
16 public class DynamicProxy implements InvocationHandler {
17     private Object mObject;
18 
19     DynamicProxy(Object object) {
20         mObject = object;
21     }
22 
23     @Override
24     public Object invoke(Object proxy, Method method, Object[] objects) throws Throwable {
25         System.out.println("invoke methodName=" + method.getName());
26         method.invoke(mObject, objects);
27         return null;
28     }
29 }
30 
31 //Client
32 import java.lang.reflect.InvocationHandler;
33 import java.lang.reflect.Proxy;
34 
35 public class Client {
36     public static void main(String[] args) {
37         //創建boss類
38         IShop boss = new Boss();
39         //創建動態代理
40         InvocationHandler proxyHandler = new DynamicProxy(boss);
41         ClassLoader classLoader = boss.getClass().getClassLoader();
42         //動態創建代理類
43         IShop assitant = (IShop) Proxy.newProxyInstance(classLoader, new Class[]{IShop.class}, proxyHandler);
44         assitant.buy();
45     }
46 }

運行結果

invoke methodName=buy
I am boss,I buy buy buy

       在動態代理類DynamicProxy中,聲明瞭一個Object的引用,該應用指向被代理類對象(該實例中指向的就是Boss類對象),我們調用被代理類對象(Boss對象)的具體方法(該例子中指buy方法)會在invoke方法中執行。在Client類中的Proxy.newProxyInstance()來生成動態代理類對象assistant,而調用assistant.buy()方法時會調用DynamicProxy類中的invoke方法,這樣就間接地調用了Boss類中的buy()方法,從而實現了動態代理。

 

四、靜態代理和動態代理對比

       說到使用動態代理的好處,可能得一大篇文章才說得清楚,在這裏我只想提一點,就是動態代理下,直到運行時纔會生成代理類,當代理場景比較多時,可以節約很多不必要的浪費。這裏我們比較一下,有多個代理場景時,靜態代理和動態代理的表現。

    1、靜態代理實現多個代理場景

 1 public interface IShop {
 2     void buy();
 3 }
 4 
 5 public class Boss implements IShop {
 6     @Override
 7     public void buy() {
 8         System.out.println("I am boss,I buy buy buy");
 9     }
10 }
11 
12 public class Assistant implements IShop {
13     private IShop mBoss;
14     Assistant(IShop shoper) {
15         mBoss = shoper;
16     }
17 
18     @Override
19     public void buy() {
20         mBoss.buy();
21     }
22 }
23 
24 public interface IDrive {
25     void drive();
26 }
27 
28 public class Leader implements IDrive {
29     @Override
30     public void drive() {
31         System.out.println("I am leader,I drive drive drive");
32     }
33 }
34 
35 public class Driver implements IDrive {
36     private IDrive mLeader;
37 
38     Driver(IDrive driver) {
39         mLeader = driver;
40     }
41 
42     @Override
43     public void drive() {
44         mLeader.drive();
45     }
46 }
47 
48 public class ProxyDemo {
49     public static void main(String[] args) {
50         IShop boss = new Boss();
51         IShop assitant = new Assistant(boss);
52         assitant.buy();
53 
54         IDrive leader = new Leader();
55         IDrive driver = new Driver(leader);
56         driver.drive();
57     }
58 }

運行結果

I am boss, I buy buy buy
I am leader,I drive drive drive

     如上代碼實際上就是兩個代理場景的疊加,增加一個場景,代碼量增加一倍。

    2、動態代理實現多個代理場景

 1 public interface IShop {
 2     void buy();
 3 }
 4 
 5 public class Boss implements IShop {
 6     @Override
 7     public void buy() {
 8         System.out.println("I am boss,I buy buy buy");
 9     }
10 }
11 
12 public interface IDrive {
13     void drive();
14 }
15 
16 public class Leader implements IDrive {
17     @Override
18     public void drive() {
19         System.out.println("I am leader,I drive drive drive");
20     }
21 }
22 
23 //動態代理類
24 import java.lang.reflect.InvocationHandler;
25 import java.lang.reflect.Method;
26 
27 public class DynamicProxy implements InvocationHandler {
28     private Object mObject;
29 
30     DynamicProxy(Object object) {
31         mObject = object;
32     }
33 
34     @Override
35     public Object invoke(Object proxy, Method method, Object[] objects) throws Throwable {
36         System.out.println("invoke methodName=" + method.getName());
37         method.invoke(mObject, objects);
38         return null;
39     }
40 }
41 
42 //Client
43 import java.lang.reflect.InvocationHandler;
44 import java.lang.reflect.Proxy;
45 
46 public class Client {
47     public static void main(String[] args) {
48         IShop boss = new Boss();
49         InvocationHandler proxyHandler = new DynamicProxy(boss);
50         ClassLoader classLoader = boss.getClass().getClassLoader();
51         IShop assitant = (IShop) Proxy.newProxyInstance(classLoader, new Class[]{IShop.class}, proxyHandler);
52         assitant.buy();
53     }
54 }

運行結果

invoke methodName=buy
I am boss, I buy buy buy
invoke methodName=drive
I am leader,I drive drive drive

       由於不需要單獨實現代理類,多個代理場景下實際上就節約了很多的代碼量。

 

結語

       代理模式作爲設計模式中的一種,使用比較廣泛,內容非常多,筆者知識有限,暫時先介紹這麼多,希望能幫助初學者能夠掌握代理模式的基本知識和使用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章