天天看點

Spring AOP源碼分析(二)JDK動态代理和CGLIB介紹

本篇是介紹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>&lt;dependency&gt;</code>

<code>        </code><code>&lt;groupid&gt;cglib&lt;/groupid&gt;</code>

<code>        </code><code>&lt;artifactid&gt;cglib&lt;/artifactid&gt;</code>

<code>        </code><code>&lt;version&gt;</code><code>3.1</code><code>&lt;/version&gt;</code>

<code>    </code><code>&lt;/dependency&gt;</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>&lt;t&gt; 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了。