本文來自:曹勝歡部落格專欄。轉載請註明出處:http://blog.csdn.net/csh624366188
代理設計模式
代理是一種常用的設計模式,其目的就是為其他對象提供一個代理以控制對某個對象的訪問。代理類負責為委託類預先處理訊息,過濾訊息並轉寄訊息,以及進行訊息被委託類執行後的後續處理。
代理模式的作用是:為其他對象提供一種代理以控制對這個對象的訪問。在某些情況下,一個客戶不想或者不能直接引用另一個對象,而代理對象可以在用戶端和目標對象之間起到中介的作用。
代理模式一般涉及到的角色有:
抽象角色:聲明真實對象和代理對象的共同介面;
代理角色:代理對象角色內部含有對真實對象的引用,從而可以操作真實對象,同時代理對象提供與真實對象相同的介面以便在任何時刻都能代替真實對象。同時,代理對象可以在執行真實對象操作時,附加其他的操作,相當於對真實對象進行封裝。
真實角色:代理角色所代表的真實對象,是我們最終要引用的對象
圖 1. 代理模式類圖
為了保持行為的一致性,代理類和委託類通常會實現相同的介面,所以在訪問者看來兩者沒有絲毫的區別。通過代理類這中間一層,能有效控制對委託 類對象的直接存取,也可以很好地隱藏和保護委託類對象,同時也為實施不同控制策略預留了空間,從而在設計上獲得了更大的靈活性。Java 動態代理機制以巧妙的方式近乎完美地實踐了代理模式的設計理念。
java動態代理
相關的類和介面
要瞭解 Java 動態代理的機制,首先需要瞭解以下相關的類或介面:
· java.lang.reflect.Proxy:這是 Java 動態代理機制的主類,它提供了一組靜態方法來為一組介面動態地組建代理程式類及其對象。
清單 1. Proxy 的靜態方法
// 方法 1: 該方法用於擷取指定代理對象所關聯的調用處理器static InvocationHandler getInvocationHandler(Object proxy) // 方法 2:該方法用於擷取關聯於指定類裝載器和一組介面的動態代理類的類對象static Class getProxyClass(ClassLoader loader, Class[] interfaces) // 方法 3:該方法用於判斷指定類對象是否是一個動態代理類static boolean isProxyClass(Class cl) // 方法 4:該方法用於為指定類裝載器、一組介面及調用處理器產生動態代理類執行個體static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)
java.lang.reflect.InvocationHandler:這是調用處理器介面,它自訂了一個 invoke 方法,用於集中處理在動態代理類對象上的方法調用,通常在該方法中實現對委託類的代理訪問。
清單 2. InvocationHandler 的核心方法
// 該方法負責集中處理動態代理類上的所有方法調用。第一個參數既是代理類執行個體,第二個參數是被調用的方法對象// 第三個方法是調用參數。調用處理器根據這三個參數進行預先處理或指派到委託類執行個體上發射執行Object invoke(Object proxy, Method method, Object[] args)
每次產生動態代理類對象時都需要指定一個實現了該介面的調用處理器對象(參見 Proxy 靜態方法 4 的第三個參數)。
· java.lang.ClassLoader:這是類裝載器類,負責將類的位元組碼裝載到 Java 虛擬機器(JVM)中並為其定義類對象,然後該類才能被使用。Proxy 靜態方法產生動態代理類同樣需要通過類裝載器來進行裝載才能使用,它與普通類的唯一區別就是其位元組碼是由 JVM 在運行時動態產生的而非預存在於任何個 .class 檔案中。
每次產生動態代理類對象時都需要指定一個類裝載器對象(參見 Proxy 靜態方法 4 的第一個參數)
代理機制及其特點
首先讓我們來瞭解一下如何使用 Java 動態代理。具體有如下四步驟:
1. 通過實現 InvocationHandler 介面建立自己的調用處理器;
2. 通過為 Proxy 類指定 ClassLoader 對象和一組 interface 來建立動態代理類;
3. 通過反射機制獲得動態代理類的建構函式,其唯一參數類型是調用處理器介面類型;
4. 通過建構函式建立動態代理類執行個體,構造時調用處理器對象作為參數被傳入。
清單 3. 動態代理對象建立過程
// InvocationHandlerImpl 實現了 InvocationHandler 介面,並能實現方法調用從代理類到委託類的指派轉寄// 其內部通常包含指向委託類執行個體的引用,用於真正執行指派轉寄過來的方法調用InvocationHandler handler = new InvocationHandlerImpl(..); // 通過 Proxy 為包括 Interface 介面在內的一組介面動態建立代理類的類對象Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... }); // 通過反射從產生的類對象獲得建構函式對象Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class }); // 通過建構函式對象建立動態代理類執行個體Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler });
實際使用過程更加簡單,因為 Proxy 的靜態方法 newProxyInstance 已經為我們封裝了步驟 2 到步驟 4 的過程,所以簡化後的過程如
清單 4. 簡化的動態代理對象建立過程
// InvocationHandlerImpl 實現了 InvocationHandler 介面,並能實現方法調用從代理類到委託類的指派轉寄InvocationHandler handler = new InvocationHandlerImpl(..); // 通過 Proxy 直接建立動態代理類執行個體Interface proxy = (Interface)Proxy.newProxyInstance( classLoader, new Class[] { Interface.class }, handler );
下面我們來看一個簡單實現動態代理的例子:
1.代理類和真實類介面:
public interface Subject{public void request();}
2.真實類:
public class RealSubject implements Subject{public void request(){System.out.println("From real subject!");}}
3.具體代理類:
import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;public class DynamicSubject implements InvocationHandler{private Object sub;public DynamicSubject(Object obj){this.sub = obj;}public Object invoke(Object proxy, Method method, Object[] args)throws Throwable{System.out.println("before calling: " + method);method.invoke(sub, args); System.out.println(args == null); System.out.println("after calling: " + method);return null;}
註:該代理類的內部屬性是Object類型,實際使用的時候通過該類的構造方法傳遞進來一個對象。 此外,該類還實現了invoke方法,該方法中的method.invoke其實就是調用被代理對象的將要 執行的方法,方法參數是sub,表示該方法從屬於sub,通過動態代理類,我們可以在執行真實對象的方法前後加入自己的一些額外方法。
4.用戶端調用樣本:
import java.lang.reflect.InvocationHandler;import java.lang.reflect.Proxy;public class Client{public static void main(String[] args){RealSubject realSubject = new RealSubject();InvocationHandler handler = new DynamicSubject(realSubject);Class<?> classType = handler.getClass();// 下面的代碼一次性組建代理程式Subject subject = (Subject) Proxy.newProxyInstance(classType.getClassLoader(), realSubject.getClass().getInterfaces(),handler);subject.request();System.out.println(subject.getClass());}}
接下來讓我們來瞭解一下 Java 動態代理機制 Proxy 的構造方法:
清單 6. Proxy 構造方法
// 由於 Proxy 內部從不直接調用建構函式,所以 private 類型意味著禁止任何調用private Proxy() {} // 由於 Proxy 內部從不直接調用建構函式,所以 protected 意味著只有子類可以調用protected Proxy(InvocationHandler h) {this.h = h;}
接著,可以快速探索一下 newProxyInstance 方法,因為其相當簡單:
清單 7. Proxy 靜態方法 newProxyInstance
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { // 檢查 h 不為空白,否則拋異常 if (h == null) { throw new NullPointerException(); } // 獲得與制定類裝載器和一組介面相關的代理類類型對象 Class cl = getProxyClass(loader, interfaces); // 通過反射擷取建構函式對象並組建代理程式類執行個體 try { Constructor cons = cl.getConstructor(constructorParams); return (Object) cons.newInstance(new Object[] { h }); } catch (NoSuchMethodException e) { throw new InternalError(e.toString()); } catch (IllegalAccessException e) { throw new InternalError(e.toString()); } catch (InstantiationException e) { throw new InternalError(e.toString()); } catch (InvocationTargetException e) { throw new InternalError(e.toString()); } }
由此可見,動態代理真正的關鍵是在 getProxyClass 方法,該方法負責為一組介面動態地組建代理程式類類型對象。
有很多條理由,人們可以否定對 class 代理的必要性,但是同樣有一些理由,相信支援 class 動態代理會更美好。介面和類的劃分,本就不是很明顯,只是到了 Java 中才變得如此的細化。如果只從方法的聲明及是否被定義來考量,有一種兩者的混合體,它的名字叫抽象類別。實現對抽象類別的動態代理,相信也有其內在的價值。此 外,還有一些曆史遺留的類,它們將因為沒有實現任何介面而從此與動態代理永世無緣。如此種種,不得不說是一個小小的遺憾。
但是,不完美並不等於不偉大,偉大是一種本質,Java 動態代理就是佐例。