讀古今文學網 > 編寫高質量代碼:改善Java程序的151個建議 > 建議106:動態代理可以使代理模式更加靈活 >

建議106:動態代理可以使代理模式更加靈活

Java的反射框架提供了動態代理(Dynamic Proxy)機制,允許在運行期對目標類生成代理,避免重複開發。我們知道一個靜態代理是通過代理主題角色(Proxy)和具體主題角色(Real Subject)共同實現抽像主題角色(Subject)的邏輯的,只是代理主題角色把相關的執行邏輯委託給了具體主題角色而已,一個簡單的靜態代理如下所示:


//抽像主題角色

interface Subject{

//定義一個方法

public void request();

}

//具體主題角色

class RealSubject implements Subject{

//實現方法

public void request(){

//業務邏輯處理

}

}

//代理主題角色

class Proxy implements Subject{

//要代理哪個實現類

private Subject subject=null;

//默認被代理者

public Proxy(){

subject=new RealSubject();

}

//通過構造函數傳遞被代理者

public Proxy(Subject_subject){

subject=_subject;

}

//實現接口中定義的方法

public void request(){

before();

subject.request();

after();

}

//預處理

private void before(){

//do something

}

//善後處理

private void after(){

//do something

}

}


這是一個簡單的靜態代理。Java還提供了java.lang.reflect.Proxy用於實現動態代理:只要提供一個抽像主題角色和具體主題角色,就可以動態實現其邏輯的,其示例代碼如下:


//抽像主題角色

interface Subject{

//定義一個方法

public void request();

}

//具體主題角色

class RealSubject implements Subject{

//實現方法

public void request(){

//業務邏輯處理

}

}

class SubjectHandler implements InvocationHandler{

//被代理的對象

private Subject subject;

public SubjectHandler(Subject_subject){

subject=_subject;

}

//委託處理方法

public Object invoke(Object proxy, Method method, Objectargs)

throws Throwable{

//預處理

System.out.println("預處理");

//直接調用被代理類的方法

Object obj=method.invoke(subject, args);

//後處理

System.out.println("後處理");

return obj;

}

}


注意看,這裡沒有了代理主題角色,取而代之的是SubjectHandler作為主要的邏輯委託處理,其中invoke方法是接口InvocationHandler定義必須實現的,它完成了對真實方法的調用。

我們來詳細瞭解一下InvocationHanlder接口,動態代理是根據被代理的接口生成所有方法的,也就是說給定一個(或多個)接口,動態代理會宣稱「我已經實現該接口下的所有方法了」,那各位讀者想想看,動態代理怎麼才能實現代理接口中的方法呢?在默認情況下所有方法的返回值都是空的,是的,雖然代理已經實現了它,但是沒有任何的邏輯含義,那怎麼辦?好辦,通過InvocationHandler接口的實現類來實現,所有方法都是由該Handler進行處理的,即所有被代理的方法都由InvocationHandler接管實際的處理任務。

我們接著來看動態代理的場景類,代碼如下:


public static void main(Stringargs){

//具體主題角色,也就是被代理類

Subject subject=new RealSubject();

//代理實例的處理Handler

InvocationHandler handler=new SubjectHandler(subject);

//當前加載器

ClassLoader cl=subject.getClass().getClassLoader();

//動態代理

Subject proxy=(Subject)Proxy.newProxyInstance(cl, subject.getClass().

getInterfaces(),handler);

//執行具體主題角色方法

proxy.request();

}


此時就實現了不用顯式創建代理類即實現代理的功能,例如可以在被代理角色執行前進行權限判斷,或者執行後進行數據校驗。

動態代理很容易實現通用的代理類,只要在InvocationHandler的invoke方法中讀取持久化數據即可實現,而且還能實現動態切入的效果,這也是AOP(Aspect Oriented Programming)編程理念。