基礎:需要具備面向對象設計思想,多態的思想,反射的思想;
java動態代理機制的出現,使得java開發人員不用手工編寫代理類,只要簡單地指定一組接口及委托類對象,便能動態地獲得代理類。代理類會負責將所有的方法調用分派到委托對象上反射執行,在分派執行的過程中,開發人員還可以按需調整委托類對象及其功能,這是一套非常靈活有彈性的代理框架。通過閱讀本文,讀者將會對java動態代理機制有更加深入的理解。本文首先從java動態代理的運行機制和特點出發,對其代碼進行了分析,推演了動態生成類的內部實現。
代理模式的基本概念和分類
代理模式:為其他對象提供一個代理,來控制對這個對象的訪問。代理對象起到中介作用,可以去掉服務或者增加額外的服務,或者引用別人的話:“代理類負責為委托類預處理消息,過濾消息并轉發消息,以及進行消息被委托類執行后的后續處理。”
代理模式在開發中的應用場景
遠程代理:為不同地理的對象提供局域網代表對象。
虛擬代理:根據需要將資源消耗很大的對象進行延遲,真正需要的時候進行創建。比如網頁中的先顯示文字再顯示圖片。
保護代理:控制不同用戶的訪問權限。比如:只有當客戶注冊成功之后,才可以進行增刪改查等操作。
智能引用代理:提供對目標代理額外的服務。
代理模式的實現方式
使用繼承和聚合實現動態代理,哪種更好呢!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
public interface moveable { public void move(); } public class car implements moveable{ @override public void move() { try { thread.sleep( new random().nextint( 1000 )); system.out.println( "……行駛中……" ); } catch (interruptedexception e) { // todo auto-generated catch block e.printstacktrace(); } } } public class car2 extends car{ @override public void move() { //分離代碼,增加業務邏輯 long starttime=system.currenttimemillis(); system.out.println( "汽車開始行駛……" ); super .move(); long endtime=system.currenttimemillis(); system.out.println( "汽車結束行駛……時間:" +(endtime-starttime)+ "ms" ); } } |
繼承方式實現代理
moveablecar2=newcar2();
car2.move();
聚合方式實現代理
carcar=newcar();
moveablem=newcar3(car);
m.move();
總結
使用繼承方式不夠靈活,當功能疊加的時候,只能臃腫的擴展代理類;
使用聚合的方式,代理之間可以相互傳遞,靈活的組合代理;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
public class carlogproxy extends car{ @override public void move() { //分離代碼,增加業務邏輯 long starttime=system.currenttimemillis(); system.out.println( "日志開始……" ); super .move(); long endtime=system.currenttimemillis(); system.out.println( "日志結束……" ); } } public class cartimeproxy implements moveable { public cartimeproxy(car car) { super (); this .car=car; } private carcar; @override public void move() { //分離代碼,增加業務邏輯 long starttime=system.currenttimemillis(); system.out.println( "汽車開始行駛……" ); car.move(); long endtime=system.currenttimemillis(); system.out.println( "汽車結束行駛……時間:" +(endtime-starttime)+ "ms" ); } } @test : car car = new car(); cartimeproxy ctp= new cartimeproxy(car); carlogproxy clp= new carlogproxy(ctp); clp.move(); //還可以通過接口相互傳遞代理實例 carlogproxy clp1= new carlogproxy(car); cartimeproxy ctp1= new cartimeproxy(clp1); ctp1.move(); |
jdk動態代理和cglib動態代理
jdk動態代理
代理實現
如果不同的對象要實現相同功能的代理類,應該如何處置?
此時可以試著將其集成在同一個代理類中-----動態代理:實現對不同類/不同方法的代理;
大致過程如下:
java動態代理類位于java.lang.reflect包下,一般主要涉及到一下兩個類:
(1)interfaceinvocationhandler:該接口中僅定義了一個方法publicobjectinvoke(objectobj,methodmethod,object[]args)
obj:一般是指代理類
method:是被代理的方法
args為該方法的參數數組。
這個抽象的方法在代理類中動態實現。
(2)proxy:該類即為動態代理類
statixobjectnewproxyinstance(classloaderloader,class[]interfaces,invocationhandlerh)
返回甙類類的一個實例,返回后的代理類可以當做被代理類使用(可以使用被代理類在接口中聲明過的方法);
實現實例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
@ timehandler public class timehandler implements invocationhandler { public timehandler(object target) { super (); this .target = target; } private objecttarget; /* * 參數: * proxy 被代理對象 * method 被代理對象的方法 * args 方法的參數 * * 返回值: * object 方法返回值 */ @override public object invoke(object proxy, method method,object[] args) throws throwable { long starttime=system.currenttimemillis(); system.out.println( "汽車開始行駛……" ); method.invoke(target); long endtime=system.currenttimemillis(); system.out.println( "汽車結束行駛……時間:" +(endtime-starttime)+ "ms" ); return null ; } } |
1
2
3
4
5
|
@被代理類的接口 public interface moveable { public void move(); } @被代理的類 |
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public class car implements moveable{ @override public void move() { try { thread.sleep( new random().nextint( 1000 )); system.out.println( "……行駛中……" ); } catch (interruptedexception e) { // todo auto-generated catch block e.printstacktrace(); } } } |
@測試
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
public class test { /** * jdk動態代理的測試類 */ public static void main(string[] args) { car car= new car(); invocationhandler h= new timehandler(car); class <?>cls=car.getclass(); /* * loader 類加載器 * interfaces 實現接口 * h invocationhandler */ moveable m=(moveable)proxy.newproxyinstance(cls.getclassloader(),cls.getinterfaces(),h); m.move(); } } |
&&測試結果
梳理總結
所為的dynamicproxy是這樣一種class:
它是在運行時生成的class,該class需要實現一組interface,使用動態代理類的時候,必須實現invocationhandler接口。
jdk動態代理的一般步驟
1.創建一個實現接口invocationhandler的類,它必須實現invoke()
2.創建被代理的類以及接口
3.調用proxy的靜態方法,創建一個代理類
newproxyinstance(classloaderloader,class[]interfaces,invocationhandlerh)
4.通過代理調用方法
cglib動態代理的實現
代理實現
@引入cglib-node-2.2.jar包
@cglibproxy攔截類實現接口methodinterceptor:重寫intercept攔截方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
public class cglibproxy implements methodinterceptor { private enhancerenhancer= new enhancer(); public object getproxy( class cl) { //設置創建子類的類 enhancer.setsuperclass(cl); enhancer.setcallback( this ); return enhancer.create(); } /* * 攔截所有目標類方法的調用 * object 目標類的實例 * m 目標方法的反射對象 * args 方法的參數 * proxy 代理類的實例 * */ @override public object intercept(object obj, method m,object[] args, methodproxy proxy) throws throwable { system.out.println( "日志開始……" ); //代理類調用父類的方法 proxy.invokesuper(obj, args); system.out.println( "日志結束……" ); return null ; } } |
@被代理類train
1
2
3
4
5
6
|
public class train { public void move() { system.out.println( "火車行駛中……" ); } } |
@測試類
1
2
3
4
5
6
7
8
9
10
|
public class test { /** * cglibproxy動態代理測試類 */ public static void main(string[] args) { cglibproxy proxy= new cglibproxy(); train t=(train)proxy.getproxy(train. class ); t.move(); } } |
##測試結果:
梳理總結
使用cglibproxy實現動態代理的一般步驟
1、創建類實現接口methodinterceptor,并重寫intercept方法
2、創建被代理類
3、調用代理類自定義的方法,得到一個代理實例
4、通過代理實例調用被代理類的需要執行的方法
比較總結
jdk動態代理
1、只能代理實現了接口的類
2、沒有實現接口的類不能實現jdk的動態代理
cglib動態代理
1、針對類來實現代理
2、對執行目標類產生一個子類,通過方法攔截技術攔截所有父類方法的調用。
模擬代理產生步驟
思路:
實現功能:通過proxy的newproxyinstance返回代理對象
1、聲明一段源碼(動態產生代理)
2、編譯源碼(jdkcompilerapi)產生新的類(代理類)
3、將這個類load到內存當中,產生一個新的對象(代理對象)
4、返回代理對象
完善動態代理實現
首先得到系統編譯器,通過編譯器得到文件管理者,然后獲取文件,然后編譯器執行編譯任務,完成編譯之后,將class文件加載到類加載器中,通過構造方法得到實例,然后調用newinstance()接收一個對象的實例。
(1)拿到編譯器javacompilercompiler=toolprovider.getsystemjavacompiler();
(2)文件管理者standardjavafilemanagerfilemgr=compiler.getstandardfilemanager(null,null,null);
(3)獲取文件iterableunits=filemgr.getjavafileobjects(filename);
(4)編譯任務compilationtaskt=compiler.gettask(null,filemgr,null,null,null,units);
(5)load到內存
classloadercl=classloader.getsystemclassloader();
classc=cl.loadclass(”com.imooc.proxy.$proxy0”);
(6)通過代理對象的構造器構造實例
constructorctr=c.getconstructor(infce);
ctr.newinstance(newcar());
-------
上說所說,內部的業務邏輯是硬編碼的,如何實現真正的動態代理,動態的指定業務邏輯呢?
1、需要創建一個事務處理器,首先創建一個接口也就是invocationhandler,為了模擬jdk,這里把接口的名字和jdk事務處理器名稱一樣,同樣寫一個方法叫做invoke(),用來表示對某個對象的某個方法進行業務處理,所以需要把某個對象以及對象的方法作為invoke()方法的參數傳遞進來,invoke(objectobj,methodmethod),方法作為參數使用到了java反射,需要把此包引入。這樣invocationhandler接口就完成了。
2、創建事務處理實現類比如說時間代理timerproxy,實現了invocationhandler接口,這樣結構就成了
1
2
3
4
5
6
7
8
|
——————timerproxyimplementsinvocationhandler{ ————————- @override ————————-voidinvoke(objectobj,methodmethod){ ——————————— //業務邏輯<br> —————————————method.invoke(目標對象,參數); ———————————— //業務邏輯<br> ——————————} —————————} |
需要將目標對象傳入,沒有參數可以不寫參數,創建代理對象的構造方法,初始化目標對象
3、在proxy類的newproxyinstance()方法中,除了要把目標class接口作為參數外,還需要把事務處理器invocationhandler傳進去,然后更改創建實例對象中硬編碼的部分用事務處理器方法替代即可。難點在于字符串的拼接。
總結
在我們項目中代理模式有自己的實際意義,比如說我們想要調用某個jar包下的某個類,可以在調用這個類之前之后添加一些特殊的業務邏輯,這種方式也叫作aop面向切面編程。(在不改變原有功能的基礎上,添加額外的功能。)
以上就是本文關于java動態代理(設計模式)代碼詳解的全部內容,希望對大家有所幫助。感興趣的朋友可以繼續參閱本站其他相關專題,如有不足之處,歡迎留言指出。感謝朋友們對本站的支持!
原文鏈接:http://blog.csdn.net/csdn_terence/article/details/52860221