前言
外掛化開發所涉及到的技術點非常多,比如程式的啟動流程、四大元件啟動流程、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
由於不需要單獨實現代理類,多個代理場景下實際上就節約了很多的程式碼量。
結語
代理模式作為設計模式中的一種,使用比較廣泛,內容非常多,筆者知識有限,暫時先介紹這麼多,希望能幫助初學者能夠掌握代理模式的基本知識和使用。