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

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