動態代理模式

代理模式

代理是基本的設計模式之一,它是你爲了提供額外的或不同的操作,而插入的用來代替“實際”對象的對象。這些操作通常涉及與“實際”對象的通信,因此代理通常充當着中間人的角色。

基本代理

下面是一個用來展示代理結構的簡單示例:
public interface Interface {
void doSomething();
 
void somethingElse(String arg);
}
class RealObject implements Interface {
 
@Override
public void doSomething() {
System.out.println("doSomething");
}
 
@Override
public void somethingElse(String arg) {
System.out.println("somethingElse " + arg);
}
}
 
class SimpleProxy implements Interface {
private Interface proxied;
public SimpleProxy(Interface proxied) {
this.proxied = proxied;
}
 
@Override
public void doSomething() {
System.out.println("doSomething");
proxied.doSomething();
}
 
@Override
public void somethingElse(String arg) {
System.out.println("somethingElse " + arg);
proxied.somethingElse(arg);
}
}
 
public class SimpelProxyDemo {
public static void consumer(Interface iface) {
iface.doSomething();
iface.somethingElse("bonobo");
}
public static void main(String[] args) {
consumer(new RealObject());
consumer(new SimpleProxy(new RealObject()));
}
 
}
因爲consumer()接受的Interface,所以它無法知道正在獲得的到底是RealObject還是SimpleProxy,因爲這二者都實現了Interface。但是SimpleProxy已經被插入到了客戶端和RealObject之間,因此它會執行操作,然後調用RealObject上相同的方法。
在任何時刻,只要你想要將額外的操作從“實際”對象中分離到不同的地方,特別是當你希望能夠很容易地做出修改,從沒有使用額外操作轉爲使用這些操作,或者反過來時,代理就顯得很有用(設計模式的關鍵就是封裝修改----因此你需要修改事務以證明這種模式的正確性)。例如,如果你希望跟蹤對RealObject中的方法的調用,或者希望度量這些調用的開銷,那麼你應該怎麼做呢?這些代碼肯定是你不希望將其合併到應用中的代碼,因此代理使得你可以很容易地添加或移除它們。

動態代理

Java的動態代理比代理的思想更向前邁進了一步,因爲它可以動態地創建代理並動態地處理對所代理方法的調用。在動態代理上所做的所有調用都會被重定向到單一的調用處理器上,它的工作是揭示調用的類型並確定相應的對策。下面是用動態代理重寫的SimpleProxyDemo.java
class DynamicProxyHandler implements InvocationHandler {
private Object proxied;
public DynamicProxyHandler(Object proxied) {
this.proxied = proxied;
}
// 調用處理器 
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("**** proxy: " + proxy.getClass() + ", method: " + method + ", args" + args);
if(args != null) {
for(Object arg : args) {
System.out.println(" " + arg);
}
}
return method.invoke(proxied, args);
}
}
 
public class SimpelProxyDemo {
public static void consumer(Interface iface) {
iface.doSomething();
iface.somethingElse("bonobo");
}
public static void main(String[] args) {
// consumer(new RealObject());
// consumer(new SimpleProxy(new RealObject()));
RealObject real = new RealObject();
consumer(real);
Interface proxy = (Interface) Proxy.newProxyInstance(Interface.class.getClassLoader(), new Class[]{Interface.class}, new DynamicProxyHandler(real));
consumer(proxy);
}
 
}
通過調用靜態方法Proxy.newProxyInstance()可以創建動態代理,這個方法需要得到一個類加載器(你通常可以從已經被加載的對象中獲取其類加載器,然後傳遞給它),一個你希望該代理實現的接口列表(不是類或抽象類),以及InvocationHandler接口的一個實現。動態代理可以將所有調用重定向到調用處理器,因此通常會向調用處理器的構造器傳遞給一個“實際”對象的引用,從而使得調用處理器在執行其中介任務時,可以將請求轉發。
invoke()方法中傳遞進來了代理對象,以防你需要區分請求的來源,但是在許多情況下,你並不關心這一點。然而,在invoke()內部,在代理上調用方法時需要格外當心,因爲對接口的調用將被重定向爲對代理的調用。

過濾代理方法

通常,你會執行被代理的操作,然後使用Method.invoke()將請求轉發給被代理對象,並傳入必需的參數。這初看起來可能有些受限,就像你只能執行泛化操作一樣。但是,你可以通過傳遞其他的參數,來過濾某些方法調用:
class MethodSelector implements InvocationHandler {
private Object proxied;
public MethodSelector(Object proxied) {
this.proxied = proxied;
}
 
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if(method.getName().equals("interesting"))
System.out.println("proxy detected the interesting method");
return method.invoke(proxied, args);
}
}
 
interface SomeMethods {
void boring1();
void boring2();
void interesting(String arg);
void boring3();
}
 
class Implementation implements SomeMethods {
 
@Override
public void boring1() {
System.out.println("boring1");
}
 
@Override
public void boring2() {
System.out.println("boring2");
}
 
@Override
public void interesting(String arg) {
System.out.println("interesting arg:" + arg);
}
 
@Override
public void boring3() {
System.out.println("boring3");
}
}
 
public class SimpelProxyDemo {
public static void main(String[] args) {
SomeMethods proxy = (SomeMethods) Proxy.newProxyInstance(SomeMethods.class.getClassLoader(), new Class[]{SomeMethods.class}, new MethodSelector(new Implementation()));
proxy.boring1();
proxy.boring2();
proxy.interesting("bonobo");
proxy.boring3();
}
}
這裏,我們只查看了方法名,但是你還可以查看方法簽名的其他方面,甚至可以搜索特定的參數值。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章