JAVA設計模式(24) —代理(Proxy)模式__JAVA

來源:互聯網
上載者:User

定義:為其他對象提供一種代理以控制對這個對象的訪問。

類型:對象結構型模式

類圖:



Proxy模式涉及的角色

抽象主題角色(Subject):聲明了代理主題和真實主題的公用介面,使任何需要真實主題的地方都能用代理主題代替。 代理主題角色(Proxy):含有真實主題的引用,從而可以在任何時候操作真實主題。代理主題功過提供和真實主題相同的介面,使它可以隨時代替真實主題。代理主題通過持有真實主題的引用,不但可以控制真實主題的建立或刪除,還可以在真實主題被調用前進行攔截或在調用後進行某些操作.。 真實代理對象(RealSubject):定義了代理角色(Proxy)所代表的具體對象。

為什麼我們要控制對象的存取權限呢。其中一個原因是通過控制來延遲物件的建立和執行個體化(lazy instantiation),直到真正需要使用該對象才進行建立和執行個體化。由於一些對象建立和執行個體化需要佔用大量系統資源,但我們並不能確定使用者一定會調用該對象,所以通過延遲物件執行個體化來減緩系統資源的消耗。例如文檔編輯器如word,我們可以在裡面插入連結、圖片等,但是並不是我們每次開啟word時都有建立和執行個體化這些對象,特別是執行個體化圖片對象很消耗資源,而且我們有必要執行個體化所有圖片嗎。當我們在查看word時,只是看到其中的一部分,所以沒有必要執行個體化所以資源,當我們看下一頁時再執行個體化也不遲。



代碼實現

抽象角色(Subject)

interface Subject{public void request();}

真實角色(RealSubject)

class RealSubject implements Subject{@Overridepublic void request() {System.out.println("request...");}}

代理角色(Proxy)

class ProxySubject implements Subject{private RealSubject realSubject;// 以真實角色作為代理角色的屬性  @Overridepublic void request() {preRequest();if(realSubject==null){realSubject = new RealSubject();}realSubject.request();// 此處執行真實對象的request方法  postRequest();}public void preRequest(){//預先處理System.out.println("before request...");}public void postRequest(){//後處理System.out.println("after request...");}}

客戶角色(Client)

public class ProxyClient {public static void main(String[] args) {Subject subject = new ProxySubject();subject.request();}}


從上面的例子可以看出代理模式的工作方式。首先,因為代理主題和真實主題都實現了共同的介面。這使我們可以在不改變原來介面的情況下,只要用真實主題對象的地方,都可以用代理主題來代替。其次,代理主題在客戶和真實主題之間起了一個中介作用。利用這個中介平台,我們可以在把客戶請求傳遞給真實主題之前(或之後)做一些必要的預先處理(AOP)


Java對代理模式的支援 ---動態代理 
上面的代理,我們強迫代理類RedWineProxy實現了抽象介面SellInterface.這導致我們的代理類無法通用於其他介面,所以不得不為每一個介面實現一個代理類.幸好,java為代理模式提供了支援. 
Java主要是通過Proxy類和InvocationHandler介面來給實現對代理模式的支援的。

Java代理機制實現代碼:

/** * 代理類一定要實現了InvocationHandler介面 */public class ProxyObject implements InvocationHandler {private Object proxy_obj;ProxyObject(Object obj) {this.proxy_obj = obj;}public static Object factory(Object obj) {Class cls = obj.getClass();// 通過Proxy類的newProxyInstance方法來返回代理對象return Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(), new ProxyObject(obj));}/** * 實現InvocationHandler介面的invoke */public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {System.out.println("函數調用前被攔截了:   " + method);if (args != null) {// 列印參數列表System.out.println("方法有  " + args.length + "    個參數");for (int i = 0; i < args.length; i++) {System.out.println(args[i]);}}// 利用反射機制動態調用原對象的方法Object mo = method.invoke(proxy_obj, args);System.out.println("函數調用後進行處理 :   " + method);return mo;}// 測試代碼public static void main(String agr[]) {ISubject subject = (ISubject) factory(new ProxySubject());subject.request();}}

通過上面的代碼可以看出,代理主題ProxyObject類並沒有實現我們定義的ISubject介面,而是實現了Java的InvocationHandler介面。這樣就把代理主題角色和我們的業務代碼分離開來,使代理對象能通用於其他介面。
其實InvocationHandler介面就是一種攔截機制。當系統中有了代理對象以後,對原對象(真實主題)方法的調用都會轉由InvocationHandler介面來處理,並把方法資訊以參數的形式傳遞給invoke方法。這樣,我們就可以在invoke方法中攔截原對象的調用,並通過反射機制來動態調用原對象的方法。這好象也是spring aop編程的基礎吧。


下面實現一個簡單的AOP攔截機制

這個例子可以攔截我們指定的函數,並在攔截前後根據需要進行處理

切面介面,定義我們調用前後的處理方法

/** * 切面介面,通過實現這個介面,我們可以對指定函數在調用前後進行處理 */interface AopInterface {public void before(Object obj);// 調用的處理public void end(Object obj);// 調用後的處理}

切面介面實作類別,實現處理邏輯

class AopInterfaceImp implements AopInterface {@Overridepublic void before(Object obj) {//add your operation hereSystem.out.println("調用前攔截");}@Overridepublic void end(Object obj) {//add your operation hereSystem.out.println("調用調用後處理");}}

代理類實現代碼:

class ProxyObject implements InvocationHandler {private AopInterface aop;// 定義了切入時調用的方法private Object proxy_obj;private String methodName;// 指定要切入的方法名ProxyObject() {}public Object factory(Object obj) {proxy_obj = obj;Class cls = obj.getClass();return Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(), this);}public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {if (this.aop == null)throw new NullPointerException("aop is null");if (method == null)throw new NullPointerException("method is null");Object o;// 如果指定了要攔截方法名,並且調用的方法和指定的方法名相同,則進行攔截處理// 否則當正常方法處理if (methodName != null && method.toString().indexOf(methodName) != -1) {aop.before(proxy_obj);// 指定方法調用前的處理o = method.invoke(proxy_obj, args);aop.end(proxy_obj);// 指定方法調用後的處理} else {// 沒有指定的方法,以正常方法調用o = method.invoke(proxy_obj, args);}return o;}public AopInterface getAop() {return aop;}public void setAop(AopInterface aop) {this.aop = aop;}public String getMethodName() {return methodName;}public void setMethodName(String methodName) {this.methodName = methodName;}}

目標介面(需要被攔截對象)代碼:

// 目標介面(需要被攔截的介面)interface SubInterface {public void login(String userName, String password);public void access(String res);}

目標介面實作類別代碼:

class SubInterfaceImpl implements SubInterface {@Overridepublic void login(String userName, String password) {System.out.println("SubInterfaceImpl login(" + userName + ","+ password + ")");}@Overridepublic void access(String res) {System.out.println("SubInterfaceImpl access(" + res + ")");}}

測試類別代碼:

public class AOPDemo {public static void main(String[] args) {ProxyObject proxy = new ProxyObject();proxy.setAop(new AopInterfaceImp());// 我們實現的攔截處理對象proxy.setMethodName("access");// 指定要攔截的函數SubInterface si = (SubInterface) proxy.factory(new SubInterfaceImpl());// 因為login方法不是我們指定的攔截函數,AopInterfaceImp是不會被執行si.login("tt", "12345");// access是我們指定的攔截方法,所以調用access的前後會先執行AopInterfaceImp// 對象的兩個方法si.access("authentication");}}



通過上面可以看出,攔截機制是代理模式的重要使用方式之一。

除了攔截,代理模式還常用於資源載入。當我們要載入的資源很大時,我們可以讓真實主題角色在後台載入資源,讓代理主題角色負責處理前台的等待提示資訊。
還有就是授權機制。通過代理能攔截真實主題的能力,來控制真實主題的存取權限。



適用情境

在需要用比較通用和複雜的對象指標代替簡單的指標的時候,使用 Proxy模式。下面是一些可以使用Proxy模式常見情況:

遠程(Remote)代理:為一個位於不同的地址空間的對象提供一個局域代表對象。這個不同的地址空間可以是在本機器中,也可是在另一台機器中。遠程代理又叫做大使(Ambassador)。好處是系統可以將網路的細節隱藏起來,使得用戶端不必考慮網路的存在。客戶完全可以認為被代理的對象是局域的而不是遠端,而代理對象承擔了大部份的網路通訊工作。由於客戶可能沒有意識到會啟動一個耗費時間的遠程調用,因此客戶沒有必要的思想準備。 虛擬(Virtual)代理(圖片消極式載入的例子):根據需要建立一個資源消耗較大的對象,使得此對象只在需要時才會被真正建立。使用虛擬代理模式的好處就是代理對象可以在必要的時候才將被代理的對象載入;代理可以對載入的過程加以必要的最佳化。當一個模組的載入十分耗費資源的情況下,虛擬代理的好處就非常明顯。 保護代理(Protection Proxy):控制對原始對象的訪問。保護代理用於對象應該有不同的存取權限的時候。 Copy-on-Write代理:虛擬代理的一種。把複製(複製)拖延到只有在用戶端需要時,才真正採取行動。 智能引用(Smart Reference)代理:當一個對象被引用時,提供一些額外的操作,比如將對此對象調用的次數記錄下來等。







聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.