對于動態(tài)代理,學(xué)過aop的應(yīng)該都不會陌生,因為代理是實現(xiàn)aop功能的核心和關(guān)鍵技術(shù)。那么今天我們將開始動態(tài)代理的學(xué)習(xí):
一、引出動態(tài)代理
生活中代理應(yīng)該是很常見的,比如你可以通過代理商去買電腦,也可以直接找廠商買電腦,最終都是買到了電腦。程序中也一樣存在代理的情況,比如要為已經(jīng)存在的多個具有相同接口的目標(biāo)類的各個方法增加一些系統(tǒng)功能,例如:異常處理、日志、計算方法耗時等等,那么我們會怎么做呢?
1.會編寫一個與目標(biāo)類擁有相同接口的代理類,代理類的每個方法調(diào)用目標(biāo)類的相同方法,然后在調(diào)用方法前后加上系統(tǒng)功能所需要的代碼。
2.如果采用工廠模式或者配置文件的方式進(jìn)行管理,則不需要修改客戶端程序,在配置文件中配置是使用目標(biāo)類、還是代理類,這樣以后很容易切換。
樣例如下:
1
2
3
4
5
|
public class x{ public void sayhello(){ syso:say hello; } } |
現(xiàn)在我要在這個方法之前添加一個時間,方法之后添加一個時間,計算這個方法執(zhí)行的時間一共是多少.如果我沒有得到sayhello源碼,那么我怎么做呢?寫一個代理:
1
2
3
4
5
6
7
8
9
10
|
public class xproxy { private x x; public void sayhello { starttime: x.syhello(); endtime; } } |
說明:上面的是偽代碼。
把開始時間和結(jié)束時間放在這個方法的前后就可以了.
通常我們讓兩個方法實現(xiàn)同一個接口.那么client想用x也可以,想用xproxy也可以了.具體的原理圖,如下圖所示:
二、創(chuàng)建動態(tài)代理類
現(xiàn)在試想一下,上面只是代理了一個目標(biāo)類,如果多個目標(biāo)類,那么是不是要創(chuàng)建n多個代理類呢?那樣不是代碼太不靈活且笨重了。當(dāng)然不會。
java虛擬機(jī)可以在運(yùn)行期間動態(tài)生成類,這種類是以字節(jié)碼的形式生成出來的。這種動態(tài)生成的類往往呢就是代理類。即動態(tài)代理類。
jvm生成的動態(tài)代理類必須滿足一定的條件,這就是必須實現(xiàn)一個或多個接口。所以jvm生成的動態(tài)代理只能用作具有相同接口的目標(biāo)類的代理。(動態(tài)生成的類不是代理,我們只是吧這個類當(dāng)成代理來用。)
proxy動態(tài)代理的api:
兩個參數(shù)應(yīng)該很容易理解:
第一個參數(shù):我們知道任何一個字節(jié)碼都是需要通過類加載器來加載的,那么這個動態(tài)生成的字節(jié)碼也不例外,需要給它一個類加載器。
第二個參數(shù):就是動態(tài)代理類生成,必須滿足的一個條件,需要實現(xiàn)一個或者多個接口,否則這個生成的類字節(jié)碼中就沒有方法了,沒有方法就失去了其功能意義。
下面我們動手來創(chuàng)建一個動態(tài)的代理類,大體思路為:
1.創(chuàng)建實現(xiàn)collection接口的動態(tài)類和查看其名稱,分析proxy.getproxyclass方法的各個參數(shù)
2.編碼列出動態(tài)類中的所有構(gòu)造方法和參數(shù)簽名
3.編碼列出動態(tài)類中的所有方法和參數(shù)簽名
4.創(chuàng)建動態(tài)類的實例對象:1)用反射獲取構(gòu)造方法 2)編寫一個最簡單的invocationhandle類 3)調(diào)用構(gòu)造方法創(chuàng)建動態(tài)類的實例對象,并將編寫的invocationhandle類的實例對象傳遞進(jìn)去 4)打印創(chuàng)建對象和調(diào)用對象的沒有返回的方法和getclass方法,演示調(diào)用其他有返回值方法報告了異常。
5)將創(chuàng)建動態(tài)類的實例對象的代理寫成匿名內(nèi)部類方式,簡化代碼。
樣例分步實現(xiàn)如下:
(1)首先我們來完成前面3步:
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
package study.javaenhance; import java.lang.reflect.constructor; import java.lang.reflect.method; import java.lang.reflect.proxy; import java.util.collection; public class proxytest { public static void main(string[] args) { class clazzproxy1 = proxy.getproxyclass(collection. class .getclassloader(),collection. class ); system.out.println(clazzproxy1.getname()); //上面輸出的為一個類,那么一個類肯定有其構(gòu)造方法和方法,下面我們來列出來 system.out.println( "----------begin constructors list----------" ); //1.列出構(gòu)造方法 constructor[] constructors = clazzproxy1.getconstructors(); for (constructor constructor : constructors) { string name = constructor.getname(); stringbuilder sbuilder = new stringbuilder(name); sbuilder.append( '(' ); class [] clazzparams = constructor.getparametertypes(); for ( class clazzparam : clazzparams) { sbuilder.append(clazzparam.getname()).append( ',' ); } if (clazzparams!= null && clazzparams.length != 0 ) sbuilder.deletecharat(sbuilder.length()- 1 ); sbuilder.append( ')' ); system.out.println(sbuilder.tostring()); } //2.列出這個類字節(jié)碼中的所有方法 system.out.println( "----------begin methods list----------" ); method[] methods = clazzproxy1.getmethods(); for (method method : methods){ string name = method.getname(); stringbuilder sbuilder = new stringbuilder(name); sbuilder.append( '(' ); class [] clazzparams = method.getparametertypes(); for ( class clazzparam : clazzparams){ sbuilder.append(clazzparam.getname()).append( ',' ); } if (clazzparams!= null && clazzparams.length != 0 ) sbuilder.deletecharat(sbuilder.length()- 1 ); sbuilder.append( ')' ); system.out.println(sbuilder.tostring()); } } } |
輸出結(jié)果如下:
$proxy0
----------begin constructors list----------
$proxy0(java.lang.reflect.invocationhandler)
----------begin methods list----------
add(java.lang.object)
hashcode()
clear()
equals(java.lang.object)
tostring()
contains(java.lang.object)
isempty()
addall(java.util.collection)
iterator()
size()
toarray([ljava.lang.object;)
toarray()
remove(java.lang.object)
containsall(java.util.collection)
removeall(java.util.collection)
retainall(java.util.collection)
isproxyclass(java.lang.class)
getproxyclass(java.lang.classloader,[ljava.lang.class;)
getinvocationhandler(java.lang.object)
newproxyinstance(java.lang.classloader,[ljava.lang.class;,java.lang.reflect.invocationhandler)
wait()
wait(long,int)
wait(long)
getclass()
notify()
notifyall()
可以看到所有的方法均是來自collection 和父類object中的方法,符合我們的預(yù)期結(jié)果。接下來我們進(jìn)入第四步和第五步的實現(xiàn):
首先,我們來創(chuàng)建這個動態(tài)代理類的實例。那么直接clazzproxy1.newinstance();可不可以呢?顯然是不可以的嘛.我們剛剛說了,動態(tài)生成的這個代理類只有一個構(gòu)造方法,有沒有無參構(gòu)造方法呢?沒有啊.所以,創(chuàng)建 一個參數(shù)的構(gòu)造方法.參數(shù)類型是java.lang.reflect.invocationhandler。
下面我們來實現(xiàn):
1.定義一個上述接口的實例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
package study.javaenhance; import java.lang.reflect.invocationhandler; import java.lang.reflect.method; public class myinvocationhandler implements invocationhandler { @override public object invoke(object proxy, method method, object[] args) throws throwable { // todo auto-generated method stub return null ; } } |
2.調(diào)用實現(xiàn)創(chuàng)建實例動態(tài)類:
1
2
3
4
5
6
7
8
|
//3.創(chuàng)建實例對象 system.out.println( "----------begin create instance object----------" ); constructor constructor = clazzproxy1.getconstructor(invocationhandler. class ); myinvocationhandler myinvocationhandler = new myinvocationhandler(); collection collection = (collection) constructor.newinstance(myinvocationhandler); system.out.println(collection); collection.clear(); //collection.size(); //報錯,異常會發(fā)生,產(chǎn)生異常的原因在于其返回值為int類型,但是每一次調(diào)用一個方法都會調(diào)用到invoke方法,我們此時的invoke返回的為null,所以是沒有辦法轉(zhuǎn)換為int類型的。 |
3.我們采用匿名內(nèi)部類的方式優(yōu)化:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
//3.1 采用匿名內(nèi)部類方式進(jìn)行創(chuàng)建 collection collection2 = (collection) constructor.newinstance( new invocationhandler() { @override public object invoke(object proxy, method method, object[] args) throws throwable { // todo auto-generated method stub return null ; } }); system.out.println(collection2); collection2.clear(); //collection2.size(); |
4.繼續(xù)優(yōu)化:思考如果我們每次都想上面方式去創(chuàng)建動態(tài)的代理類實在有點重復(fù),那么這個是jdk的proxy類中提供了簡單的方法直接去創(chuàng)建動態(tài)代理類,方式如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
//3.3 采用proxy 中提供的簡單方法創(chuàng)建 collection collection3 = (collection)proxy.newproxyinstance(collection. class .getclassloader(), new class []{collection. class }, new invocationhandler() { private arraylist target = new arraylist(); @override public object invoke(object proxy, method method, object[] args) throws throwable { return method.invoke(target, args); } }); //system.out.println(collection3); collection3.add( "abc" ); collection3.add( "def" ); collection3.add( "hij" ); system.out.println(collection3.size()); |
三、動態(tài)代理的原理簡單分析
上面我們創(chuàng)建了動態(tài)代理類,下面我們分析下代理的原理:
下面在來看一個問題:
動態(tài)代理的工作原理圖:
對上面的這個圖,我們簡單來說說:客戶端動態(tài)生成代理類,然后調(diào)用代理類的方法,代理類內(nèi)部調(diào)用handler.invoke()方法,在invoke中呢,我們又指向的目標(biāo)類.這樣就實現(xiàn)了代理了.我客戶端調(diào)用代理的什么方法,invoke就只向目標(biāo)類的同一個方法.而在指定目標(biāo)類方法的前后呢,我們還可以做其他的操作,比如記錄日志.圖中用圈圈出來的部分就是代理類自己實現(xiàn)的功能了.這就是代理類的原理.
我們來做最后一步,將上面的動態(tài)生成的代理類,編寫可生成代理和插入通告的通用方法:
test代碼:
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
package study.javaenhance; import java.lang.reflect.constructor; import java.lang.reflect.invocationhandler; import java.lang.reflect.method; import java.lang.reflect.proxy; import java.util.arraylist; import java.util.collection; public class proxytest { public static void main(string[] args) throws exception { class clazzproxy1 = proxy.getproxyclass(collection. class .getclassloader(),collection. class ); system.out.println(clazzproxy1.getname()); //上面輸出的為一個類,那么一個類肯定有其構(gòu)造方法和方法,下面我們來列出來 system.out.println( "----------begin constructors list----------" ); //1.列出構(gòu)造方法 constructor[] constructors = clazzproxy1.getconstructors(); for (constructor constructor : constructors) { string name = constructor.getname(); stringbuilder sbuilder = new stringbuilder(name); sbuilder.append( '(' ); class [] clazzparams = constructor.getparametertypes(); for ( class clazzparam : clazzparams) { sbuilder.append(clazzparam.getname()).append( ',' ); } if (clazzparams!= null && clazzparams.length != 0 ) sbuilder.deletecharat(sbuilder.length()- 1 ); sbuilder.append( ')' ); system.out.println(sbuilder.tostring()); } //2.列出這個類字節(jié)碼中的所有方法 system.out.println( "----------begin methods list----------" ); method[] methods = clazzproxy1.getmethods(); for (method method : methods){ string name = method.getname(); stringbuilder sbuilder = new stringbuilder(name); sbuilder.append( '(' ); class [] clazzparams = method.getparametertypes(); for ( class clazzparam : clazzparams){ sbuilder.append(clazzparam.getname()).append( ',' ); } if (clazzparams!= null && clazzparams.length != 0 ) sbuilder.deletecharat(sbuilder.length()- 1 ); sbuilder.append( ')' ); system.out.println(sbuilder.tostring()); } //3.創(chuàng)建實例對象 system.out.println( "----------begin create instance object----------" ); constructor constructor = clazzproxy1.getconstructor(invocationhandler. class ); myinvocationhandler myinvocationhandler = new myinvocationhandler(); collection collection = (collection) constructor.newinstance(myinvocationhandler); system.out.println(collection); collection.clear(); //collection.size(); //報錯,異常會發(fā)生,產(chǎn)生異常的原因在于其返回值為int類型,但是每一次調(diào)用一個方法都會調(diào)用到invoke方法,我們此時的invoke返回的為null,所以是沒有辦法轉(zhuǎn)換為int類型的。 //3.1 采用匿名內(nèi)部類方式進(jìn)行創(chuàng)建 collection collection2 = (collection) constructor.newinstance( new invocationhandler() { @override public object invoke(object proxy, method method, object[] args) throws throwable { // todo auto-generated method stub return null ; } }); system.out.println(collection2); collection2.clear(); //collection2.size(); //3.3 采用proxy 中提供的簡單方法創(chuàng)建 collection collection3 = (collection)proxy.newproxyinstance(collection. class .getclassloader(), new class []{collection. class }, new invocationhandler() { private arraylist target = new arraylist(); @override public object invoke(object proxy, method method, object[] args) throws throwable { return method.invoke(target, args); } }); //system.out.println(collection3); collection3.add( "abc" ); collection3.add( "def" ); collection3.add( "hij" ); system.out.println(collection3.size()); system.out.println(collection3.getclass().getname()); //這個返回的是 system.out.println( "----------begin create instance object 抽化----------" ); //3.4抽出動態(tài)代理讓目標(biāo)對象和切面對象都是傳入進(jìn)去的 final arraylist target = new arraylist(); collection collection4 = (collection)getproxy(target, new myadvice()); collection4.add( "test1" ); collection4.add( "test2" ); system.out.println(collection4.size()); } private static object getproxy( final object target, final advice advice) { object object = proxy.newproxyinstance(target.getclass().getclassloader(), target.getclass().getinterfaces(), new invocationhandler() { @override public object invoke(object proxy, method method, object[] args) throws throwable { advice.beforemethod(method); object retvalue = method.invoke(target, args); advice.aftermethod(method); return retvalue; } }); return object; } } |
四、實現(xiàn)類似spring的可配置的aop框架
首先,我們要完成的要求如下:
我們來模擬spring的工廠模式讀取從配置文件傳遞過來的類。如果這個類是一個普通類則直接返回。如果是一個代理類,則創(chuàng)建代理對象,返回代理類。
具體的理論知識可以看上面的圖片
首先創(chuàng)建一個beanfactory.java類。這是一個bean工廠,用于讀取配置文件中的類
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
package study.javaenhance.aopframework; import java.io.ioexception; import java.io.inputstream; import java.util.properties; import study.javaenhance.advice; public class beanfactory { private properties properties = new properties(); public beanfactory(inputstream instream) { try { properties.load(instream); } catch (ioexception e) { e.printstacktrace(); } finally { if (instream != null ) { try { instream.close(); } catch (ioexception e) { e.printstacktrace(); } } } } public object getbean(string name) { string classname = properties.getproperty(name); object bean = null ; try { class clazz = class .forname(classname); bean = clazz.newinstance(); if (bean instanceof proxyfactorybean) { proxyfactorybean proxybean = (proxyfactorybean) bean; advice advice = (advice) class .forname(properties.getproperty(name + ".advice" )).newinstance(); object target = class .forname(properties.getproperty(name + ".target" )).newinstance(); proxybean.setadvice(advice); proxybean.settarget(target); object proxy = proxybean.getproxy(); return proxy; } } catch (exception e) { // todo auto-generated catch block e.printstacktrace(); } return bean; } } |
如果這個類中包含proxyfactorybean,則調(diào)用proxyfactorybean中的getproxy方法。動態(tài)生成代理類。
proxyfactorybean.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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
package study.javaenhance.aopframework; import java.lang.reflect.invocationhandler; import java.lang.reflect.method; import java.lang.reflect.proxy; import study.javaenhance.advice; public class proxyfactorybean { private object target; private advice advice; public object gettarget() { return target; } public void settarget(object target) { this .target = target; } public advice getadvice() { return advice; } public void setadvice(advice advice) { this .advice = advice; } public object getproxy() { object object = proxy.newproxyinstance(target.getclass().getclassloader(),target.getclass().getinterfaces(), new invocationhandler() { @override public object invoke(object proxy, method method, object[] args) throws throwable { advice.beforemethod(method); object retvalue = method.invoke(target, args); advice.aftermethod(method); return retvalue; } }); return object; } } |
接下來創(chuàng)建一個config.properties文件.config.properties
xxx=java.util.arraylist
#xxx=study.javaenhance.aopframework.proxyfactorybean
xxx.advice=study.javaenhance.myadvice
xxx.target=java.util.arraylist
最后建立測試類:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
package study.javaenhance.aopframework; import java.io.inputstream; import java.util.collection; public class aopframeworktest { public static void main(string[] args) { inputstream ips = aopframeworktest. class .getresourceasstream( "config.properties" ); object bean = new beanfactory(ips).getbean( "xxx" ); system.out.println(bean.getclass().getname()); ((collection)bean).clear(); } } |
測試ok
總結(jié):整個java增強(qiáng)的視頻學(xué)習(xí)完成了,一共記住了多少我也不知道.但知道了很多內(nèi)在的知識,如果有時間的話,或者說過一段時間可以拿出來問下,提供自己的技能。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持服務(wù)器之家。
原文鏈接:http://www.cnblogs.com/pony1223/p/7719766.html