小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

代理模式 PROXY Surrogate 結(jié)構型 設計模式(十四)

 新進小設計 2020-07-19
代理模式 PROXY 別名Surrogate

意圖

為其他的對象提供一種代理以控制對這個對象的訪問。
代理模式含義比較清晰,就是中間人,中介公司,經(jīng)紀人...
在計算機程序中,代理就表示一個客戶端不想或者不能夠直接引用一個對象
而代理對象可以在客戶端和目標對象之間起到中介的作用

結(jié)構

代理模式的根本在于隔離,如下圖所示,間接訪問
image_5c074938_56cd
代理對象如何能夠真的代理真實對象?
在Java語言中,看起來像的一個方式就是實現(xiàn)同一接口
image_5c074938_5f89
 
代理角色和真實對象角色擁有共同的抽象類型,他們擁有相同的對外接口request()方法
ProxySubject內(nèi)部擁有一個RealSubject
你應該能感覺到組合模式的思想-----他們都是Subject,屬于同一個Component
對外有一致的接口
抽象主題角色Subject
聲明了真實主題和代理主題的共同接口,任何使用真實主題的地方,都可以使用代理主題
代理主題角色ProxySubject
代理主題角色內(nèi)部含有對真實對象的引用,從而可以在任何時候操作真實主題
代理主題提供與真實主題的相同的接口,以便任何時刻,都可以替代真實主題
而且,代理主題還可以在真實主題執(zhí)行前后增加額外的處理,比如:經(jīng)紀人要先收下費~
真實主題角色RealSubject
被代理的真實主題對象,真正工作的是他,比如經(jīng)紀人總不會站在舞臺上去~

示例代碼

Subject 抽象角色 定義了真正的處理請求 的request()方法 
package proxy;public interface Subject {void request();
}
RealSubject真實主題角色,實現(xiàn)了處理請求的方法
package proxy;public class RealSubject implements Subject {
@Overridepublic void request() {
    System.out.println("realSubject process request....");
}
}
Proxy代理角色
實現(xiàn)了request()方法,用于替代真實主題,內(nèi)部調(diào)用真實主題完成請求
并且額外的提供了pre和after操作
package proxy;public class Proxy implements Subject{private Subject realSubject;
    @Overridepublic void request() {
        preRequest();
    realSubject.request();
        afterRequest();
    }     public Proxy(Subject realSubject){this.realSubject = realSubject;
    }public void preRequest(){
        System.out.println("pre request do sth....");
    }     public void afterRequest(){
        System.out.println("after request do sth....");
    }
}
測試類
package proxy;public class Test {/**請求subject執(zhí)行請求
* @param subject*/public static void askForSth(Subject subject){
    subject.request();
    System.out.println("################");
 }   
public static void main(String[] args){
     Subject real = new RealSubject();
     Subject proxy = new Proxy(real);
     askForSth(proxy);
     askForSth(real);
    }
}
定義了真實對象,也定義了一個代理對象
查看他們分別處理請求的結(jié)果
image_5c074938_777b
 
從下面的時序圖中,能更好的感受到“間接”的感覺
在真正調(diào)用真實對象方法前,需要先執(zhí)行preRequest方法
真實對象方法調(diào)用后,在執(zhí)行afterRequest方法
image_5c074939_77de

代理實現(xiàn)

代理的實現(xiàn)分類有兩種,靜態(tài)代理和動態(tài)代理
前面形式描述的代理,就是靜態(tài)代理
在編譯時期,就已經(jīng)編寫生成好了代理類的源代碼,程序運行之前class文件就已經(jīng)生成了
這種按照我們上面模式編寫了代理類和真實類的形式就是 靜態(tài)代理
靜態(tài)代理經(jīng)常被用來對原有邏輯代碼進行擴展,原有的邏輯不需要變更,但是可以增加更多的處理邏輯
但是,但是如果有很多的對象需要被代理怎么辦?
如果按照靜態(tài)代理的形式,那么將會出現(xiàn)很多的代理類,勢必導致代碼的臃腫。
所以后來出現(xiàn)了動態(tài)代理

JDK代理機制

所謂動態(tài)代理,按照字面意思就是動態(tài)的進行代理,動態(tài)相對于靜態(tài)的含義是不需要事先主動的創(chuàng)建代理類,可以在運行時需要的時候,動態(tài)的創(chuàng)建一個代理類。
動態(tài)代理的動態(tài)關鍵在于代理類的動態(tài)生成,不需要我們實現(xiàn)創(chuàng)建,從class文件的角度來看的話,是與靜態(tài)代理一樣的,仍舊有一個代理類的Class文件
在Java中提供了內(nèi)置的動態(tài)代理的支持。
Java在java.lang.reflect包中提供了三個核心 Proxy,InvocationHandlerMethod  可以用于動態(tài)代理的使用
 
Java動態(tài)代理簡單示例
package proxy.MyDynamicProxy;public interface Subject {void doSth();
}
package proxy.MyDynamicProxy;public class RealSubject implements Subject {
@Overridepublic void doSth() {
System.out.println("real Object do something...");
}
}
package proxy.MyDynamicProxy;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;public class DynamicProxyHandler implements InvocationHandler {private Object realSubject;public DynamicProxyHandler(Object realSubject) {this.realSubject = realSubject;
}
@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("proxy do something....");return method.invoke(realSubject, args);
}
}
package proxy.MyDynamicProxy;import java.lang.reflect.Proxy;public class Test {public static void main(String[] args){
RealSubject realSubject = new RealSubject();
Subject proxy = (Subject) Proxy
.newProxyInstance(Test.class.getClassLoader(), new Class[]{Subject.class}, new DynamicProxyHandler(realSubject));
proxy.doSth();
}
}
測試結(jié)果為:
image_5c074939_41fd
 
動態(tài)代理到底都做了什么?
對于靜態(tài)代理,我們有一個RealSubject,以及他的超接口Subject
Subject定義了方法,RealSubject實現(xiàn)了方法。
然后我們創(chuàng)建了代理類,這個代理類實現(xiàn)了Subject接口,并且將新增的邏輯添加進來,然后通過代理類進行方法調(diào)用。 
 
在上面的例子中,RealSubject,以及他的超接口Subject含義不變,與靜態(tài)代理中的邏輯一樣。
然后我們創(chuàng)建了一個調(diào)用處理器DynamicProxyHandler 實現(xiàn)了 InvocationHandler接口
該接口只有一個方法invoke
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
他有三個參數(shù)
proxy - 在其上調(diào)用方法的代理實例
method - 對應于在代理實例上調(diào)用的接口方法的 Method 實例。Method 對象的聲明類將是在其中聲明方法的接口,該接口可以是代理類賴以繼承方法的代理接口的超接口。
args - 包含傳入代理實例上方法調(diào)用的參數(shù)值的對象數(shù)組,如果接口方法不使用參數(shù),則為 null?;绢愋偷膮?shù)被包裝在適當基本包裝器類(如 java.lang.Integer 或 java.lang.Boolean)的實例中。
 
最后通過Java提供的代理機制創(chuàng)建了一個代理
    Subject proxy = (Subject) Proxy
        .newProxyInstance(Test.class.getClassLoader(), new Class[]{Subject.class}, new DynamicProxyHandler(realSubject));
核心就是newProxyInstance方法,他創(chuàng)建了一個實現(xiàn)了Subject接口的代理類
public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
                               throws IllegalArgumentException
這個方法也有三個參數(shù)
loader - 定義代理類的類加載器
interfaces - 代理類要實現(xiàn)的接口列表
h - 指派方法調(diào)用的調(diào)用處理程序
 
為什么需要這三個參數(shù)呢?
首先,Proxy.newProxyInstance幫你動態(tài)的創(chuàng)建方法,肯定要有一個類加載器,上面的示例中我們直接使用的測試類的類加載,這個一般是應用程序  類加載器
再者,動態(tài)代理與靜態(tài)代理一樣,需要實現(xiàn)同樣的接口,那你實現(xiàn)了哪些接口呢?所以你得把接口列表告訴我
最后,你希望有哪些處理呢?你要把處理器給我
 
proxy.doSth();執(zhí)行時,會將當前代理實例,以及當前方法,以及當前方法的參數(shù)傳遞給invoke方法,所以就完成代理的功能。
 
再來重頭理一下:
  1. 如同靜態(tài)代理,需要被代理的對象RealSubject,以及他的超接口Subject

  2. 需要實現(xiàn)InvocationHandler接口創(chuàng)建一個處理器,新增加的方法邏輯封裝在invoke方法中

  3. Proxy.newProxyInstance創(chuàng)建代理實例

  4. 使用創(chuàng)建的代理實例執(zhí)行方法

簡言之,動態(tài)代理與靜態(tài)代理一模一樣,差別就在于不用你事先去自己主動地創(chuàng)建一個代理類
靜態(tài)的時候編寫了代理類,然后編譯為class然后需要時被加載到JVM,然后調(diào)用
動態(tài)是運行時在需要的時候,直接生成class文件
 
依照上面的步驟流程,你就可以借助于Java的機制實現(xiàn)動態(tài)代理
但是你會發(fā)現(xiàn),Proxy.newProxyInstance方法的參數(shù)需要一個 Class<?>[] interfaces,這意味著什么?這意味著被代理的對象必須實現(xiàn)一個接口
如果被代理的對象不曾實現(xiàn)任何接口怎么辦?
給每個被代理的對象增加一個標記接口(形式接口)?如果只是為了使用JDK的動態(tài)代理實現(xiàn),而添加了無意義的接口這是否妥當?

CGLIB

還有另外一種形式的動態(tài)代理CGLIB
需要兩個Jar  
image_5c074939_55fa
package proxy.cglib;public class RealSubject{public void doSth() {
System.out.println("realSubject process request....");
}
}
package proxy.cglib;import java.lang.reflect.Method;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy;public class MyHandler implements MethodInterceptor {
@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy)throws Throwable {
System.out.println("before do something...");
Object object = methodProxy.invokeSuper(o,objects);
System.out.println("after do something...");return object;
}
}
package proxy.cglib;import net.sf.cglib.proxy.Enhancer;public class Test {public static void main(String[] args){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(RealSubject.class);
enhancer.setCallback(new MyHandler());
RealSubject subject = (RealSubject)enhancer.create();
subject.doSth();
}
}
在這個示例中,不再需要接口,僅僅只有一個真是對象RealSubject
實現(xiàn)了一個處理器 MyHandler 繼承自 MethodInterceptor,實現(xiàn)了intercept方法
在測試客戶端中,通過四個步驟創(chuàng)建了代理對象,然后借助于代理對象執(zhí)行
image_5c074939_666
 
從    enhancer.setSuperclass(RealSubject.class);這一句或許猜得到,CGLIB不依賴于接口,而是代理類繼承了真實主題類
 
流程
真實主題對象RealSubject是必不可少的,否則代理模式就沒有意義了
類似JDK的代理模式,處理器也是解耦的,在CGLIB中借助于MethodInterceptor接口約定,這一步要做的事情的本質(zhì)與InvocationHandler并沒有什么太多不同---封裝附加的處理邏輯
借助于Enhancer用來組裝處理創(chuàng)建邏輯,并且創(chuàng)建代理類
setSuperclass設置需要繼承的類(也就是被代理的類)
setCallback設置回調(diào)函數(shù)
create創(chuàng)建真正的代理對象。
 
CGLIB采用繼承的機制,如果一個類是final的怎么辦?那就歇菜了

JDK代理機制與CGLIB對比

目前到JDK8 據(jù)說性能已經(jīng)優(yōu)于CGLIB了
JDK機制不需要第三方Jar,JDK默認集成,CGLIB需要引入第三方Jar包
JDK需要依賴真實主題對象實現(xiàn)接口,CGLIB則不需要,CGLIB繼承了真實主題
CGLIB雖然不依賴真實主題實現(xiàn)接口,但是被代理的類不能為final,那樣的類是無法繼承的
通常的做法是如果實現(xiàn)了接口,那么使用JDK機制,如果沒有實現(xiàn)接口,使用CGLIB

代理用途分類

代理模式的根本在于隔離,“間接”,只要隔離,間接,那么就可以隱藏真實對象,并且增加額外的服務,優(yōu)化,管理等
比如
隱藏了真實的對象,比如你通過中介租房子,可能到期也沒見過房東
 
提供了代理層,可以提供更多服務
比如買賣房屋通過中介可以節(jié)省你合同的審校工作,很多人不懂合同中暗藏的貓膩
 
隱藏真實對象,自然能夠起到一定的保護作用,避免了直接接觸
比如去學校見孩子,需要先經(jīng)過老師同意
 
通過代理,也相當于有一個管家,可以管理外界對真實對象的接觸訪問
比如,真實對象是電腦,管家類軟件相當于代理,可以限制小孩子對電腦的使用時長
 
圍繞著代理帶來的特點“隱藏真實對象,并且增加額外的服務,優(yōu)化,限制”
在多種場景下,延伸出來一些分類
遠程代理 Remote
為一個位于不同的地址空間的對象提供一個局域代表對象,這個不同的地址空間可以是本機器的,也可以是另一臺機器的
虛擬代理 Virtual
根據(jù)需要創(chuàng)建一個資源消耗較大的對象,使得此對象只在需要時才會被真正創(chuàng)建
保護代理 Protect or Access
控制對一個對象的訪問,如果需要,可以給不同的用戶提供不同級別的使用權限
Cache代理
為一個目標操作的結(jié)果提供臨時的存儲空間,以便多個客戶端可以共享這些結(jié)果 
防火墻代理 Firewall
保護目標,防止惡意行為
同步代理 Synchronization
使幾個用戶能夠同時使用一個對象而沒有沖突

智能引用 Smart Reference
當一個對象被引用時,提供一些額外的操作,比如將對象調(diào)用次數(shù)記錄下來
 
很顯然,這些分類其實只是代理的不同應用場景,以后可能還會有更多的分類出來
但是永遠也脫離不了代理的“隔離”“間接”的根本核心。

總結(jié)

代理角色雖然是真實角色的“代理人”,雖然代理角色內(nèi)部依賴真實角色
但是真實角色可以完全脫離代理人,單獨出現(xiàn)
比如上面示例中的

        askForSth(proxy);

        askForSth(real); 

只不過,通過代理角色會有不同的效果
 
代理人只是會“幫助”解決他能解決的問題,它能提供的服務,他做不了的事情
比如經(jīng)紀人不會出唱片,對于出唱片的任務還是會委托給真實角色
現(xiàn)實世界中,我們通常說真實角色委托代理角色,比如,房東找中介
在程序世界中,通常卻說代理角色將任務委托給真實角色,委托與被委托都是相對的
要看你到底是站在什么視角看待問題,無所謂~
 
再次強調(diào),代理模式的重點在于增加對真實受訪對象的控制,也可以增加額外的服務。

    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多