外掛化開發筆記(一)代理模式

宋者為王發表於2020-07-26

前言

      外掛化開發所涉及到的技術點非常多,比如程式的啟動流程、四大元件啟動流程、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

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

 

結語

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

相關文章