一,什麼是代理模式
Proxy 模式 又叫代理模式,是構造型的設計模式之一,它可以爲其他對象提供一種代理(Proxy)以控制對這個對象的訪問。
所謂代理,是指具有與代理元,(被代理的對象) 具有相同的接口的類,客戶端必須通過代理與被代理的目標類交互,而代理一般在交互的過程中(交互前後),進行某些特別的處理
二,靜態代理模式的結構
subject (抽象主題角色):真實主題與代理主題的共同接口。
RealSubject(真實主題角色): 定義了代理角色所代表的真實對象
Proxy(代理主題角色):含有對真實主題角色的引用,代理角色通常在將客戶端調用傳遞給真實主題對象之前或者之後執行某些操作,而不是單純返回真實主題對象。
代理模式的定義:由於某些原因需要給某對象提供一個代理以控制對該對象的訪問。這時,訪問對象不適合或者不能直接引用目標對象,代理對象作爲訪問對象和目標對象之間的中介。
代理模式的主要優點有:
- 代理模式在客戶端與目標對象之間起到一箇中介作用和保護目標對象的作用;
- 代理對象可以擴展目標對象的功能;
- 代理模式能將客戶端與目標對象分離,在一定程度上降低了系統的耦合度;
其主要缺點是:
- 在客戶端和目標對象之間增加一個代理對象,會造成請求處理速度變慢;
- 增加了系統的複雜度;
三,靜態代碼實現:
subject (抽象主題角色):真實主題與代理主題的共同接口。
/**
* subject (抽象主題角色):真實主題與代理主題的共同接口。
*/
public interface Subject {
public void sailBook();
}
RealSubject(真實主題角色): 定義了代理角色所代表的真實對象
public class RealSubject implements Subject{
@Override
public void sailBook() {
System.out.println("賣書");
}
}
Proxy(代理主題角色):
public class ProxySubject implements Subject{
private RealSubject realSubject ;
public RealSubject getRealSubject() {
return realSubject;
}
public void setRealSubject(RealSubject realSubject) {
this.realSubject = realSubject;
}
@Override
public void sailBook() {
discount();
this.realSubject.sailBook();
give();
}
public void discount(){
System.out.println("打折");
}
public void give(){
System.out.println("贈送代金券");
}
}
測試:
public class Test {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
ProxySubject proxySubject = new ProxySubject();
proxySubject.setRealSubject(realSubject);
proxySubject.sailBook();
}
}
四:動態代理
在前面介紹的代理模式中,代理類中包含了對真實主題的引用,這種方式存在兩個缺點。
- 真實主題與代理主題一一對應,增加真實主題也要增加代理。
- 設計代理以前真實主題必須事先存在,不太靈活。採用動態代理模式可以解決以上問題,如 SpringAOP,其結構圖如圖 4 所示。
- InvocationHandler : 是代理實例的調用處理程序實現的接口。每個代理實例都具有一個關聯的調用處理程序。對代理實例調用方法時,將方法調用進行編碼並將其指派到它的調用處理程序的 invoke 方法
- invoke :
在代理實例上處理方法調用並返回結果。 - Proxy.newProxyInstance() : 創建代理實例
代碼實現:
抽象主題角色: subject
/**
* subject (抽象主題角色):真實主題與代理主題的共同接口。
*/
public interface Subject {
public void sailBook();
}
RealSubject(真實主題角色): 定義了代理角色所代表的真實對象
/**
* @ClassName RealSubject
* @Description RealSubject(真實主題角色): 定義了代理角色所代表的真實對象
* @Version 1.0
**/
public class RealSubject implements Subject {
@Override
public void sailBook() {
System.out.println("賣書");
}
}
實現 InvocationHandler 代理接口, 根據反射關聯實例調用方法
/**
* @ClassName MyHandler
* @Description 實現InvocationHandler 接口 ,
* 每個代理實例都具有一個關聯的調用處理程序。對代理實例調用方法時,將對方法調用進行編碼並將其指派到它的調用處理程序的 invoke 方法。
* @Version 1.0
**/
public class MyHandler implements InvocationHandler{
private RealSubject realSubject;
public RealSubject getRealSubject() {
return realSubject;
}
public void setRealSubject(RealSubject realSubject) {
this.realSubject = realSubject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
dazhe();
/** method.invoke(Object object,Object... args )
* @param object :調用基礎方法的對象
* @param args : 用於方法調用的參數
* @return 在上使用參數調度此對象表示的方法的結果
*/
Object result = method.invoke(realSubject,args);
give();
return result;
}
public void dazhe(){
System.out.println("打折");
}
public void give(){
System.out.println("代金券");
}
}
測試
public class Test {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
MyHandler myHandler = new MyHandler();
myHandler.setRealSubject(realSubject);
/** @Method Proxy.newProxyInstance() 返回指定接口的代理類方法調用指派到指定的調用處理程序的實例
* @param RealSubject.class.getClassLoader() 裝載機 - 類加載器定義的代理類
* @param realSubject.getClass().getInterfaces() 接口 - 列表界面爲代理類來實現
* @param myHandler h- 所調用處理調度方法調用
*
* 返回代理實例與由指定的類加載器定義的代理類的指定調用處理程序和實現指定接口
*/
Subject subject = (Subject) Proxy.newProxyInstance(RealSubject.class.getClassLoader(),realSubject.getClass().getInterfaces(),myHandler);
subject.sailBook();
}
}
五:代理模式的應用場景
- 遠程代理,這種方式通常是爲了隱藏目標對象存在於不同地址空間的事實,方便客戶端訪問。例如,用戶申請某些網盤空間時,會在用戶的文件系統中建立一個虛擬的硬盤,用戶訪問虛擬硬盤時實際訪問的是網盤空間。
- 虛擬代理,這種方式通常用於要創建的目標對象開銷很大時。例如,下載一幅很大的圖像需要很長時間,因某種計算比較複雜而短時間無法完成,這時可以先用小比例的虛擬代理替換真實的對象,消除用戶對服務器慢的感覺。
- 安全代理,這種方式通常用於控制不同種類客戶對真實對象的訪問權限。
- 智能指引,主要用於調用目標對象時,代理附加一些額外的處理功能。例如,增加計算真實對象的引用次數的功能,這樣當該對象沒有被引用時,就可以自動釋放它。
- 延遲加載,指爲了提高系統的性能,延遲對目標的加載。例如,Hibernate 中就存在屬性的延遲加載和關聯表的延時加載