生活随笔
收集整理的這篇文章主要介紹了
深度解析JAVA动态代理设计模式
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
基本概念:Proxy模式也叫代理模式,所謂代理,是指具有與代理元(被代理的對象)具有相同的接口的類,客戶端必須通過代理與被代理的目標類交互,而代理一般在交互的過程中(交互前后),進行某些特別的處理(比如說,日志記錄,事務控制,權限過濾等等)。
(一)靜態(tài)代理
項目開發(fā)中,我們往往要在某些業(yè)務方法之前或者之后加上一些日志記錄,但是為了使業(yè)務方法更加專注于業(yè)務流程的實現(xiàn),我們希望夠把這些記錄日志的語句從業(yè)務方法中脫離出來,使得整個程序的結構更加清晰。那么有什么方案能夠實現(xiàn)這個設計思路呢
下面我們寫個例子看看使用靜態(tài)代理的解決方案
首先我們設計一個IHello接口:
[java]?view plaincopyprint?
public?interface?IHello{?? ??????? ???public?void?sayHello(String?name);?? }??
然后我們寫個類去實現(xiàn)上面這個接口:
[java]?view plaincopyprint?
public?class?Hello?implements?IHello{?? ?????????public?void?sayHello(String?name){?? ??????????System.out.println("你好,"+name);?? ?????????}?? }??
現(xiàn)在我們設計一個代理類為sayHello這個方法加上日志記錄:
[java]?view plaincopyprint?
public?class?ProxyHello?implements?IHello{?? ????private?IHello?hello;?? ????public?ProxyHello(IHello?hello){?? ?????????this.hello=hello;?? ????}?? ????public?void?sayHello(String?name){?? ?????????System.out.println("sayHello?start......");?? ?????????hello.sayHello(name);?? ?????????System.out.println("sayHello?end......");?? ???}?? }??
最后我們寫一個測試類:
[java]?view plaincopyprint?
public?class?Test{?? ?????public?static?void?main(String[]?args){?? ??????????IHello?hello=new?ProxyHello(new?Hello());?? ??????????hello.sayHello("極度暴走");?? ?????}?? }??
運行結果:
sayHello start.....
你好,極度暴走
sayHello end ......
從上面的代碼我們可以看出,hello對象(代理實例)是由ProxyHello這個代理所創(chuàng)建,無需修改Hello類中的sayHello方法便可為其添加日志記錄,并且,當我們以后不需要日志記錄時我們只要把生成hello對象的代碼改成如下便可:
[java]?view plaincopyprint?
public?class?Test{?? ?????public?static?void?main(String[]?args){?? ??????????IHello?hello=new?Hello();?? ??????????hello.sayHello("極度暴走");?? ?????}?? }??
(二)動態(tài)代理
設想一下,如果我們要為項目的所有類的方法加上日志記錄,那么根據(jù)靜態(tài)代理的解決方案,我們需要為所有的類設計各自的代理類,這樣顯然是很麻煩的,并且會使整個程序代碼的冗余度很高。這時我們就想,能不能有那么一個類專門用來為我們動態(tài)生成代理類,OK,動態(tài)代理就因此應運而生了
先來看下動態(tài)代理類的設計:
[java]?view plaincopyprint?
import?java.lang.reflect.InvocationHandler;?? import?java.lang.reflect.Method;?? import?java.lang.reflect.Proxy;?? ?? public?class?DynaProxy?implements?InvocationHandler{?? ?????? ????private?Object?delegate;?? ?????? ????public??Object?bind(Object?delegate){?? ????????this.delegate=delegate;?? ????????return?Proxy.newProxyInstance(this.delegate.getClass().getClassLoader(),?this.delegate.getClass().getInterfaces(),?this);?? ????}?? ?????? ????public?Object?invoke(Object?proxy,?Method?method,?Object[]?args){?? ????????Object?result=null;??????? ????????try?{?? ????????????System.out.println("mehtod?start......");?? ?????????????? ????????????result=method.invoke(delegate,?args);?? ????????????System.out.println("mehtod?end......");?? ????????}?catch?(Exception?e)?{?? ????????????e.printStackTrace();?? ????????}?? ????????return?result;?? ????}?? }??
接下來我們設計一個測試類,從測試類中我們便可以看出動態(tài)代理的優(yōu)勢:
[java]?view plaincopyprint?
public?void?Test{?? ????public?static?void?main(){?? ????????IHello?hello=new?DynaProxy().bind(new?Hello());?? ????????hello.sayHello("極度暴走");?? ?????????? ????????IGoogbye?googbye=new?DynaProxy.bind(new?Goodbye());?? ????????googbye.sayGoogbye("極度暴走");?? ????}?? }??
運行結果:
method? start.....
你好,極度暴走
method? end ......
method? start.....
再見,極度暴走
method? end ......
看出優(yōu)勢了沒?
簡而言之,動態(tài)代理為我們動態(tài)創(chuàng)建了委托類的代理實例,所有的類都可以通過DynaProxy這個代理類去生產(chǎn)代理實例,而不用為了每個類寫一個各自的代理類。
那么動態(tài)代理是如何幫我們創(chuàng)建代理實例的呢?
要解答這個問題,必須搞清楚以下幾點:
?
1? Proxy.newProxyInstance接口
InvocationHandler接口并沒有想象中的那么高深莫測,事實上,這個接口只定義了一個invoke方法,我們要求代理類去實現(xiàn)這個接口,也就是要求代理類要去實現(xiàn)這個invoke方法。上面的例子,我們在invoke這個方法中實現(xiàn)了在業(yè)務方法(method參數(shù))前后加上了日志記錄操作。很顯然,我們最后想達到的效果是,當委托類(如Hello類)的某個方法被調用時,我們能夠將這個方法對象(Method)傳遞給我們的invoke方法,然后通過調用invoke方法實現(xiàn)我們需要的代理操作。
那么invoke方法是什么時候被調用的呢?讓我們看完下面的內容再來思考這個問題。
?
2 Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)方法
這個方法實現(xiàn)了代理實例的創(chuàng)建,通過查看JDK的源代碼,我發(fā)現(xiàn)這個方法的實現(xiàn)主要分下面幾個步驟
步驟一:生成一個代理實例的類對象
Class proxyClass = getProxyClass(loader, interfaces);
步驟二:獲取proxyClass 對象中含有InvocationHandler.class參數(shù)的構造函數(shù)
Constructor cons =proxyClass? .getConstructor(InvocationHandler.class);
步驟三:調用上面的構造函數(shù),將我們設計的InvocationHandler的實現(xiàn)類傳遞進去
return (Object) cons.newInstance(new Object[] { handler });
我們來分析一下第一步生成的類對象,這里我貼出生成的代理類的源代碼(反編譯得來的):
[java]?view plaincopyprint?
import?java.lang.reflect.InvocationHandler;??? import?java.lang.reflect.Method;??? import?java.lang.reflect.Proxy;??? import?java.lang.reflect.UndeclaredThrowableException;??? ?? public?final?class?$Proxy0?extends?Proxy??? ??implements?IHello{??? ??private?static?Method?m0;??? ??private?static?Method?m1;??? ??private?static?Method?m2;??? ??private?static?Method?m3;??? ??static??? ??{???? ??????m3?=?Class.forName("com.IHello").getMethod("sayHello",?new?Class[0]);??? ???????? ??????m0?=?Class.forName("java.lang.Object").getMethod("hashCode",?new?Class[0]);??? ??????m1?=?Class.forName("java.lang.Object").getMethod("equals",?new?Class[]?{?Class.forName("java.lang.Object")?});??? ??????m2?=?Class.forName("java.lang.Object").getMethod("toString",?new?Class[0]);??? ??????return;??? ??}??? ???? ???? ???? ??public?$Proxy0(InvocationHandler?paramInvocationHandler){??? ????super(paramInvocationHandler);??? ??}??? ?? ???? ??public?final?void?sayHello(){?????? ??????this.h.invoke(this,?m3,?null);??? ??????return;??? ??}??? ?? ??public?final?boolean?equals(Object?paramObject){?? ??????return?((Boolean)this.h.invoke(this,?m1,?new?Object[]?{?paramObject?})).booleanValue();??? ??}??? ?? ??public?final?int?hashCode()???{????? ??????return?((Integer)this.h.invoke(this,?m0,?null)).intValue();??? ??}??? ???? ??public?final?String?toString(){????? ??????return?((String)this.h.invoke(this,?m2,?null));??? ??}???? }???
為了便于使代碼變得更簡介,更容易理解,上面的代碼中我把所有的異常檢測語句去掉了。
從代碼中我們可以看出,代理實例類中關聯(lián)著一個InvocationHandler類型的成員變量h,當代理實例的方法被調用時事實上是調用了h對象的invoke方法,
從而實現(xiàn)了上面我們所說的——“當委托類(如Hello類)的某個方法被調用時,我們能夠將這個方法對象(Method)傳遞給我們的invoke方法,然后通過調用invoke方法實現(xiàn)我們需要的代理操作”。
(三)動態(tài)代理的拓展應用——Spring AOP
正如上面分析,我們可以通過動態(tài)代理來為我們的業(yè)務方法加上日志記錄,同理,我們也可以通過動態(tài)代理實現(xiàn)對業(yè)務方法的事務控制(比如在方法前加上beginTransation,在方法后加上commit)。
Spring中使用AOP(面向切面編程)實現(xiàn)事務控制的原理便是:通過的動態(tài)代理的機制,動態(tài)生成我們需要的代理實例,然后通過依賴注入,在運行時將這些代理實例注入到對應的委托類的對象中。
總結
以上是生活随笔為你收集整理的深度解析JAVA动态代理设计模式的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內容還不錯,歡迎將生活随笔推薦給好友。