本篇是介紹java實作代理對象的兩種方法,jdk動态代理和cglib。
jdk動态代理:針對你所調用的方法是接口所定義的方法。動态的建立一個類,通過實作目标類的接口來實作代理。
cglib:沒有限制。通過繼承目标類來建立代理類,實作代理。
下面看案例:
案例一,jdk動态代理:
person和animals都實作了say接口sayhello方法。現在就需要對他們的sayhello方法進行攔截。
say接口如下:
<a href="http://my.oschina.net/pingpangkuangmo/blog/376303#">?</a>
1
2
3
4
<code>public</code> <code>interface</code> <code>say {</code>
<code> </code><code>public</code> <code>void</code> <code>sayhello();</code>
<code>}</code>
person類如下:
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
<code>package</code> <code>com.lg.aop.base;</code>
<code>public</code> <code>class</code> <code>person</code><code>implements</code> <code>say{</code>
<code> </code>
<code> </code><code>private</code> <code>string name;</code>
<code> </code><code>public</code> <code>person() {</code>
<code> </code><code>super</code><code>();</code>
<code> </code><code>}</code>
<code> </code><code>public</code> <code>person(string name) {</code>
<code> </code><code>this</code><code>.name = name;</code>
<code> </code><code>@override</code>
<code> </code><code>public</code> <code>void</code> <code>sayhello() {</code>
<code> </code><code>system.out.println(</code><code>"my name is "</code><code>+name+</code><code>"!"</code><code>);</code>
<code> </code><code>throw</code> <code>new</code> <code>runtimeexception();</code>
<code> </code><code>public</code> <code>string getname() {</code>
<code> </code><code>return</code> <code>name;</code>
<code> </code><code>public</code> <code>void</code> <code>setname(string name) {</code>
animal類如下:
<code>public</code> <code>class</code> <code>animals</code><code>implements</code> <code>say{</code>
<code> </code><code>system.out.println(</code><code>"i am a animal"</code><code>);</code>
使用jdk動态代理來建立代理對象的工具類jdkdynamicproxy如下:
32
33
34
35
36
37
38
39
40
41
42
<code>import</code> <code>java.lang.reflect.invocationhandler;</code>
<code>import</code> <code>java.lang.reflect.method;</code>
<code>import</code> <code>java.lang.reflect.proxy;</code>
<code>public</code> <code>class</code> <code>jdkdynamicproxy {</code>
<code> </code><code>public</code> <code>static</code> <code>object createproxy(</code><code>final</code> <code>object target){</code>
<code> </code><code>return</code> <code>proxy.newproxyinstance(proxytest.</code><code>class</code><code>.getclassloader(),target.getclass().getinterfaces(),</code><code>new</code> <code>invocationhandler(){</code>
<code> </code><code>@override</code>
<code> </code><code>public</code> <code>object invoke(object proxy, method method, object[] args)</code>
<code> </code><code>throws</code> <code>throwable {</code>
<code> </code><code>if</code><code>(method.getname().equals(</code><code>"sayhello"</code><code>)){</code>
<code> </code><code>dobefore();</code>
<code> </code><code>try</code> <code>{</code>
<code> </code><code>method.invoke(target, args);</code>
<code> </code><code>}</code><code>catch</code> <code>(exception e) {</code>
<code> </code><code>dothrowing();</code>
<code> </code><code>}</code>
<code> </code><code>doafter();</code>
<code> </code><code>}</code>
<code> </code><code>return</code> <code>null</code><code>;</code>
<code> </code><code>}</code>
<code> </code><code>});</code>
<code> </code><code>private</code> <code>static</code> <code>void</code> <code>dothrowing() {</code>
<code> </code><code>system.out.println(</code><code>"aop say throw a exception"</code><code>);</code>
<code> </code><code>private</code> <code>static</code> <code>void</code> <code>dobefore() {</code>
<code> </code><code>system.out.println(</code><code>"aop before say"</code><code>);</code>
<code> </code><code>private</code> <code>static</code> <code>void</code> <code>doafter() {</code>
<code> </code><code>system.out.println(</code><code>"aop after say"</code><code>);</code>
jdk動态代理就是通過proxy.newproxyinstance來建立代理對象的:
第一個參數是classloader:因為此次代理會建立一個say接口的實作類,需要将這個類加載到jvm中,是以用到了classloader。
第二個參數是代理類要實作的所有接口:當你調用這些接口的方法時都會進行攔截。
第三個參數是invocationhandler,每次調用代理對象的方法時,都會先執行invocationhandler的invoke方法,在該方法中實作我們的攔截邏輯。
在本案例中,invocationhandler的invoke方法中,我們的攔截邏輯是這樣的,當調用sayhello方法時,進行dobefore、doafter、dothrowing等攔截。
測試類如下:
<code>public</code> <code>class</code> <code>proxytest {</code>
<code> </code><code>public</code> <code>static</code> <code>void</code> <code>main(string[] args){</code>
<code> </code><code>say say1=</code><code>new</code> <code>person(</code><code>"lg"</code><code>);</code>
<code> </code><code>say1=(say)jdkdynamicproxy.createproxy(say1);</code>
<code> </code><code>say1.sayhello();</code>
<code> </code>
<code> </code><code>system.out.println(</code><code>"-------------------------------"</code><code>);</code>
<code> </code><code>say say2=</code><code>new</code> <code>animals();</code>
<code> </code><code>say2=(say) jdkdynamicproxy.createproxy(say2);</code>
<code> </code><code>say2.sayhello();</code>
測試結果如下:
<code>aop before say</code>
<code>my name is lg!</code>
<code>aop say</code><code>throw</code> <code>a exception</code>
<code>aop after say</code>
<code>-------------------------------</code>
<code>i am a animal</code>
我們可以看到實作了相應的攔截。
這裡說明下幾個概念,
(1)proxy.newproxyinstance的傳回結果和目标對象person實作了同樣的接口,但他們之間不能互相轉化。即say1=(person)jdkdynamicproxy.createproxy(say1);是錯誤的。
(2)invocationhandler的invoke(object proxy, method method, object[] args)方法中的object proxy是proxy.newproxyinstance的傳回的代理對象,不是目标對象person,是以希望執行原目标對象的sayhello方法時,method.invoke(target, args);所傳對象是原目标對象,而不是代理對象proxy。
這就是jdk動态代理的原理,目前這些攔截都是寫死寫死的,如果我們繼續進一步改造,便也可以實作更加靈活的代理,有興趣的可以實作自己的aop。
案例二,cglib代理:
首先在pom檔案中引入cglib庫。如下:
<code><dependency></code>
<code> </code><code><groupid>cglib</groupid></code>
<code> </code><code><artifactid>cglib</artifactid></code>
<code> </code><code><version></code><code>3.1</code><code></version></code>
<code> </code><code></dependency></code>
還是針對同樣的person、animals、say接口。隻是這次建立代理對象的方式變了,如下:
43
44
45
46
47
48
<code>import</code> <code>net.sf.cglib.proxy.enhancer;</code>
<code>import</code> <code>net.sf.cglib.proxy.invocationhandler;</code>
<code>public</code> <code>class</code> <code>cglibproxy {</code>
<code> </code><code>@suppresswarnings</code><code>(</code><code>"unchecked"</code><code>)</code>
<code> </code><code>public</code> <code>static</code> <code><t> t createproxy(</code><code>final</code> <code>t t){</code>
<code> </code><code>enhancer enhancer=</code><code>new</code> <code>enhancer();</code>
<code> </code><code>enhancer.setclassloader(cglibproxy.</code><code>class</code><code>.getclassloader());</code>
<code> </code><code>enhancer.setsuperclass(t.getclass());</code>
<code> </code><code>enhancer.setcallback(</code><code>new</code> <code>invocationhandler(){</code>
<code> </code><code>object ret=</code><code>null</code><code>;</code>
<code> </code><code>ret=method.invoke(t, args);</code>
<code> </code><code>return</code> <code>ret;</code>
<code> </code>
<code> </code><code>return</code> <code>(t)enhancer.create();</code>
使用cglib的enhancer來建立,建立出的代理對象繼承了指定的class。
(1)enhancer.setclassloader:也是指定類加載器,将建立出來的新類加載到jvm中。
(2)enhancer.setsuperclass(t.getclass()):設定目标類作為代理對象的父類。
(3)enhancer.setcallback:設定一個回調函數,每次調用代理類的方法時,先執行該回調函數。
然後就是測試:
<code> </code><code>cglibtest();</code>
<code> </code><code>public</code> <code>static</code> <code>void</code> <code>cglibtest(){</code>
<code> </code><code>person p=</code><code>new</code> <code>person(</code><code>"lg"</code><code>);</code>
<code> </code><code>p=cglibproxy.createproxy(p);</code>
<code> </code><code>p.sayhello();</code>
<code> </code><code>animals animals=</code><code>new</code> <code>animals();</code>
<code> </code><code>animals=cglibproxy.createproxy(animals);</code>
<code> </code><code>animals.sayhello();</code>
p=cglibproxy.createproxy(p):由于建立出來的代理對象就是目标對象的子類,是以不用像jdk動态代理那樣建立出的代理類隻能是say類型。
運作效果如下:
和jdk動态代理一樣的效果,實作了攔截。
總結一下:
(1)當你所調用的目标對象的方法是接口所定義的方法時,可以使用jdk動态代理或者cglib。即當你的目标類雖然實作了接口,但是所調用的方法卻不是接口方法時,就無法使用jdk動态代理,因為jdk動态代理的原理就是實作和目标對象同樣的接口,是以隻能調用那些接口方法。
(2)cglib則沒有此限制,因為它所建立出來的代理對象就是目标類的子類,是以可以調用目标類的任何方法(除去final方法,final方法不可繼承),都會進行攔截。
是以springaop會優先選擇jdk動态代理,當調用方法不是接口方法時,隻能選擇cglib了。