1. 程式人生 > >java動態代理實現步驟解析

java動態代理實現步驟解析


當我們使用代理方法的時候,我們希望實現以下目的:

代理角色,內部含有對真實物件RealSubject的引用,從而可以操作真實物件。代理物件提供與真實物件相同的介面,以便在任何時刻都能代替真實物件。同時,代理物件可以在執行真實物件操作時,附加其他的操作,相當於對真實物件進行封裝。

我們常常這樣的情況,有n各主題類,但是代理類中的“前處理、後處理”都是一樣的,僅呼叫主題不同。也就是說,多個主題類對應一個代理類,共享“前處理,後處理”功能,動態呼叫所需主題,大大減小了程式規模,這就是動態代理模式的特點。比較熟悉的就是我們常說的AOP程式設計。


動態代理模式的含義可以分為三層來理解:
1、給某一個物件提供一個代理,並由代理物件控制對原物件的引用。代理物件和被代理物件具有同樣的介面,能夠像操作原物件一樣操作代理物件。(代理)
2、能夠使用同一套程式碼代理多個主題類的方法,處理前處理和後處理問題,增加程式的複用性。(複用)
3、由程式動態實現上述目的。(自動化)

下面 ,我們舉個例子一步步實現上面三個步驟:
第一步 實現靜態代理
//1.抽象介面
public interface Target {
    void doSomething();
}
//2.真實類
public class TargetReal implements Target {
    public void doSomething() {
        System.out.println("Admin do something.");
    }
}
//3.代理
public class TargetProxy implements Target{
    private Target target;
   
    public TargetProxy(Admin target) {
        super();
        this.target = target;
    }
 
    public void doSomething() {
        System.out.println("操作一");
        target.doSomething();
        System.out.println("操作二");
    }
}
//4.測試程式碼
        Target target = new TargetReal();
        Target proxy = new TargetProxy(target);
        proxy.doSomething();
以上,我們實現了對Target類中doSomething的靜態代理,我們可以在doSomething的前後新增任意邏輯。
第二步:實現複用
下面,假設我們要對許多類進行代理,但為他們新增的邏輯都是一樣的,那麼我們為他們寫下代理類的時候,這部分程式碼可以進行復用。其中,唯一變化的是
target.doSomething()這個方法。
這裡,我們通過對這個方法進行代理,實現對多個這樣方法的複用。
我們隊TargetProxy進行改造
public class TargetProxy implements Target{
    private Target target;
    private proxyHandler h;
    public TargetProxy(Admin target) {
        super();
        this.target = target;
        h = new CacheHandler();
    }
    public void doSomething() {
            Class[] argTypes = new Class[] {};
            Method method = this.getClass().getMethod("doSomething", argTypes);//獲取我們需要代理的方法
            Object[] args = new Object[] {};//傳入該方法的引數,本方法沒有引數則為空
            return h.invoke(target, method, args);
    }
}

public class proxyHandler {
    public Object invoke(Object instance,Method method,Object[] args){
        System.out.println("操作一");
        method.invoke(instance, args);
        System.out.println("操作二");
    };
}
如此一來,需要進行操作一和操作二的方法,均可以通過使用CacheHandler這個類來協助代理,實現了程式碼的複用。
第三步:通過java的反射,在執行時生成代理類
上面兩個步驟,其實我們已經能夠比較全面的體驗到代理給我們帶來的好處了。但目前為止,所有的代理類和以及裡面的方法全部需要我們手動完成,雖然實現了一部分程式碼的複用,但是工作量仍然非常大,所以,java的動態代理實現了這個步驟的自動化,其中核心就是反射。
java通過動態代理實現了類似上一步中的兩個類,其中代理類是自動生成的。
在java動態代理中,有兩個關鍵的類
1、我們需要一個Handler,用於寫下我們通用的代理方法,這個Handler繼承自InvocationHandler。也就是我們上一步驟中proxyHandler的角色。
2、Proxy類,它有個靜態方法用於生成我們的代理類,這個代理類繼承自Proxy類。也就是我們上一步中的TargetProxy。
這個生成代理類的靜態方法為
public static Object newProxyInstance(ClassLoader loader, Class<?>[]interfaces,InvocationHandler h) throws IllegalArgumentException;
從他的引數中我們可以看到三個必要條件:
1、被代理的類的ClassLoader
2、被代理類的所有介面
3、寫下代理方法的InvocationHandler物件
其中:
1、ClassLoader用於代理類的載入(將原始位元組轉換成 Class 物件,以將該檔案轉換成類)
2、介面決定了代理類的所有方法
3、InvocationHandler決定了代理類的代理方法中呼叫的方法來源。也就是上一步TragetProxy中private proxyHandler h 這個屬性。
newProxyInstance的大致步驟如下:
1、根據所有介面中的方法,生成具有該方法的二進位制檔案。
2、使用ClassLoader將二進位制檔案變成類。
通過以上兩步我們已經得到了一個和被代理類介面相同,同時繼承自Proxy的類。此時和InvocationHandler還沒有關係。
3、生成一個代理類的例項,構造時使用InvocationHandler作為入參,使其獲得屬性。(由於繼承自Proxy,所以具有該屬性)
由此得到的代理類,可以完成被代理類的所有介面。其執行的方法均為handelr類中的invoke,其中method.invoke方法實現了對被代理方法的完整引用。

具體的實現方法可以看原始碼,我在理解代理時看的這篇文章,感謝作者,順便推薦:
http://www.cnblogs.com/flyoung2008/archive/2013/08/11/3251148.html