代理模式是對象的結構模式。代理模式給某一個對象提供一個代理對象,並由代理對象控制對原對象的引用。
類圖如下:
靜態代碼源碼
public abstract class Subjects { public abstract void operate();}
public class RealSubjects extends Subjects { public void operate() { System.out.println("do something..."); }}
代理類
public class ProxySubjects extends Subjects { private RealSubjects real = new RealSubjects(); public void operate() { // 調用目標之前可以做相關操作 System.out.println("before...."); real.operate(); // 調用目標之後可以做相關操作 System.out.println("after...."); }}
用戶端
public class Client { public static void main(String[] args) { Subjects subjects = new ProxySubjects(); subjects.operate(); }}
在我們程式設計中也可使用代理模式來將由一系列無關邏輯組合在一起的代碼進行解耦合,比如業務代碼中的日誌代碼就可以在代理中進行。Spring的AOP就是典型的動態代理應用。
動態代理:
觀察上面代碼可以發現每一個代理類只能為一個介面服務,這樣一來程式開發中必然會產生過多的代理,而且,所有的代理操作除了調用的方法不一樣之外,其他的操作都一樣,則此時肯定是重複代碼。解決這一問題最好的做法是可以通過一個代理類完成全部的代理功能,那麼此時就必須使用動態代理完成。。當我們遇到效能問題時,又不想對代碼進行很大的改動,這時候就可以使用動態代理。
JDK動態代理中包含一個類和一個介面:
InvocationHandler介面:
public interface InvocationHandler {
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable;
}
參數說明:
Object proxy:指被代理的對象。
Method method:要調用的方法
Object[] args:方法調用時所需要的參數
可以將InvocationHandler介面的子類想象成一個代理的最終操作類,替換掉ProxySubject。
Proxy類:
Proxy類是專門完成代理的操作類,可以通過此類為一個或多個介面動態地產生實作類別,此類提供了如下的操作方法:
public static Object newProxyInstance(ClassLoader loader, Class
public class BookFacadeFactory implements InvocationHandler { private Object target; /** * 綁定委派物件並返回一個代理類 * @author 付玉偉 * @time 2015-3-2 下午08:52:05 * @param target * @return */ public Object bind(Object target){ this.target = target; // 取得代理對象 ,要綁定介面,這是一個缺陷,cglib彌補了這一缺陷 return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; System.out.println("事務開始..."); result = method.invoke(target, args); System.out.println("事務結束..."); return result; } public static void main(String[] args){ BookFacadeFactory proxy = new BookFacadeFactory(); BookFacade bookProxy = (BookFacade)proxy.bind(new BookFacacdeImpl()); bookProxy.addBook(); }}
JDK的動態代理機制只能代理實現了介面的類,而不能實現介面的類就不能實現JDK的動態代理,cglib是針對類來實現代理的,他的原理是對指定的目標類產生一個子類,並覆蓋其中方法實現增強,但因為採用的是繼承,所以不能對final修飾的類進行代理。
public class BookFacadeCglibProxy implements MethodInterceptor { private Object target; /** * 建立代理對象 * @author 付玉偉 * @time 2015-3-2 下午09:17:34 * @param target * @return */ public Object getInstance(Object target){ this.target = target; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(this.target.getClass()); // 回調方法 enhancer.setCallback(this); // 建立代理對象 return enhancer.create(); } /** * 回調方法 */ public Object intercept(Object obj, Method arg1, Object[] arg2, MethodProxy proxy) throws Throwable { System.out.println("事務開始..."); proxy.invoke(obj, arg2); System.out.println("事務結束..."); return null; } public static void main(String[] args){ BookFacadeCglibProxy cglib = new BookFacadeCglibProxy(); BookFacadeCglibImpl bookCglib = (BookFacadeCglibImpl)cglib.getInstance(new BookFacadeCglibImpl()); bookCglib.addBook(); }}
代理模式的應用形式:
(1)遠程代理(Remote Proxy) -可以隱藏一個對象存在於不同地址空間的事實。也使得用戶端可以訪問在遠程機器上的對象,遠程機器可能具有更好的計算效能與處理速度,可以快速響應並處理用戶端請求。
(2)虛擬代理(Virtual Proxy) – 允許記憶體開銷較大的對象在需要的時候建立。只有我們真正需要這個對象的時候才建立。
(3)寫入時複製代理(Copy-On-Write Proxy) – 用來控制對象的複製,方法是延遲物件的複製,直到客戶真的需要為止。是虛擬代理的一個變體。
(4)保護代理(Protection (Access)Proxy) – 為不同的客戶提供不同層級的目標對象存取權限
(5)緩衝代理(Cache Proxy) – 為開銷大的運算結果提供暫時儲存,它允許多個客戶共用結果,以減少計算或網路延遲。
(6)防火牆代理(Firewall Proxy) – 控制網路資源的訪問,保護主題免於惡意客戶的侵害。
(7)同步代理(SynchronizationProxy) – 在多線程的情況下為主題提供安全的訪問。
(8)智能引用代理(Smart ReferenceProxy) - 當一個對象被引用時,提供一些額外的操作,比如將對此對象調用的次數記錄下來等。
(9)複雜隱藏代理(Complexity HidingProxy) – 用來隱藏一個類的複雜集合的複雜度,並進行存取控制。有時候也稱為外觀代理(Façade Proxy),這不難理解。複雜隱藏代理和面板模式是不一樣的,因為代理控制訪問,而面板模式是不一樣的,因為代理控制訪問,而面板模式只提供另一組介面。