天天看點

Java 反射之動态代理

利用java反射機制你可以在運作期動态的建立接口的實作。java.lang.reflect.proxy類就可以實作這一功能。這個類的名字(譯者注:proxy意思為代理)就是為什麼把動态接口實作叫做動态代理。動态的代理的用途十分廣泛,比如資料庫連接配接和事物管理(transaction management)還有單元測試時用到的動态mock對象以及aop中的方法攔截功能等等都使用到了動态代理。

建立代理你可以通過使用proxy.newproxyinstance()方法建立動态代理。newproxyinstance()方法有三個參數:

1、類加載器(classloader)用來加載動态代理類。

2、一個要實作的接口的數組。

3、一個invocationhandler把所有方法的調用都轉到代理上。

如下例:

1invocationhandler handler = new myinvocationhandler();

2myinterface proxy = (myinterface) proxy.newproxyinstance(

3                            myinterface.class.getclassloader(),

4                            new class[] { myinterface.class },

5                            handler);

在執行完這段代碼之後,變量proxy包含一個myinterface接口的的動态實作。所有對proxy的調用都被轉向到實作了invocationhandler接口的handler上。有關invocationhandler的内容會在下一段介紹。

invocationhandler接口在前面提到了當你調用proxy.newproxyinstance()方法時,你必須要傳入一個invocationhandler接口的實作。所有對動态代理對象的方法調用都會被轉向到invocationhandler接口的實作上,下面是invocationhandler接口的定義:

1public interface invocationhandler{

2  object invoke(object proxy, method method, object[] args)

3         throws throwable;

4}

下面是它的實作類的定義:

1public class myinvocationhandler implements invocationhandler{

2

3  public object invoke(object proxy, method method, object[] args)

4  throws throwable {

5    //do something "dynamic"

6  }

7}

傳入invoke()方法中的proxy參數是實作要代理接口的動态代理對象。通常你是不需要他的。

invoke()方法中的method對象參數代表了被動态代理的接口中要調用的方法,從這個method對象中你可以擷取到這個方法名字,方法的參數,參數類型等等資訊。關于這部分内容可以查閱之前有關method的文章。

object數組參數包含了被動态代理的方法需要的方法參數。注意:原生資料類型(如int,long等等)方法參數傳入等價的包裝對象(如integer, long等等)。

常見用例動态代理常被應用到以下幾種情況中

     * 資料庫連接配接以及事物管理

     * 單元測試中的動态mock對象

     * 自定義工廠與依賴注入(di)容器之間的擴充卡

     * 類似aop的方法攔截器

資料庫連接配接以及事物管理spring架構中有一個事物代理可以讓你送出/復原一個事物。它的具體原理在 advanced connection and transaction demarcation and propagation一文中有較長的描述,是以在這裡我就簡短的描述一下,方法調用序列如下:

1web controller --> proxy.execute(...);

2  proxy --> connection.setautocommit(false);

3  proxy --> realaction.execute();

4    realaction does database work

5  proxy --> connection.commit();

單元測試中的動态mock對象butterfly testing工具通過動态代理來動态實作樁(stub),mock和代理類來進行單元測試。在測試類a的時候如果用到了接口b,你可以傳給a一個實作了b接口的mock來代替實際的b接口實作。所有對接口b的方法調用都會被記錄,你可以自己來設定b的mock中方法的傳回值。

而且butterfly testing工具可以讓你在b的mock中包裝真實的b接口實作,這樣所有調用mock的方法都會被記錄,然後把調用轉向到真實的b接口實作。這樣你就可以檢查b中方法真實功能的調用情況。例如:你在測試dao時你可以把真實的資料庫連接配接包裝到mock中。這樣的話就與真實的情況一樣,dao可以在資料庫中讀寫資料,mock會把對資料庫的讀寫操作指令都傳給資料庫,你可以通過mock來檢查dao是不是以正确的方式來使用資料庫連接配接,比如你可以檢查是否調用了connection.close()方法。這種情況是不能簡單的依靠調用dao方法的傳回值來判斷的。

自定義工廠與依賴注入(di)容器之間的擴充卡依賴注入容器butterfly container有一個非常強大的特性可以讓你把整個容器注入到這個容器生成的bean中。但是,如果你不想依賴這個容器的接口,這個容器可以适配你自己定義的工廠接口。你僅僅需要這個接口而不是接口的實作,這樣這個工廠接口和你的類看起來就像這樣:

1public interface imyfactory {

2  bean   bean1();

3  person person();

4  ...

5}

01public class myaction{

03  protected imyfactory myfactory= null;

04

05  public myaction(imyfactory factory){

06    this.myfactory = factory;

07  }

08

09  public void execute(){

10    bean bean = this.myfactory.bean();

11    person person = this.myfactory.person();

12  }

13

14}

當myaction類調用通過容器注入到構造方法中的imyfactory執行個體的方法時,這個方法調用實際先調用了icontainer.instance()方法,這個方法可以讓你從容器中擷取執行個體。這樣這個對象可以把butterfly container容器在運作期當成一個工廠使用,比起在建立這個類的時候進行注入,這種方式顯然更好。而且這種方法沒有依賴到butterfly container中的任何接口。

類似aop的方法攔截器spring架構可以攔截指定bean的方法調用,你隻需提供這個bean繼承的接口。spring使用動态代理來包裝bean。所有對bean中方法的調用都會被代理攔截。代理可以判斷在調用實際方法之前是否需要調用其他方法或者調用其他對象的方法,還可以在bean的方法調用完畢之後再調用其他的代理方法。

特别說明:尊重作者的勞動成果,轉載請注明出處哦~~~http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt205