天天看點

Spring中的AOP(七)——基于XML配置檔案方式的AOP

    除了前面介紹的基于jdk1.5的注解方式來定義切面,切入點和增強處理外,spring aop也允許直接使用xml配置檔案來管理它們。在jdk1.5之前,隻能使用配置檔案的方式來管理,在spring2.x後提供了一個新的aop命名空間來定義切面、切入點和增強處理。

    相比之下,使用xml配置檔案方式有如下優點:

如果沒有使用jdk1.5以上版本,隻能使用xml配置檔案的方式

對早期的spring用于來說更加習慣,而且這種方式允許使用純粹的pojo來支援aop

采用xml配置方式時,我們可以清晰的看到系統中存在哪些切面

    同時,xml配置檔案的方式也有如下缺點:

不能将切面,切入點和增強處理等封裝到一個地方。當我們需要檢視切面、切點和增強處理時,必須同時結合java檔案和xml配置檔案

xml配置檔案方式比@aspectj方式有更多限制:僅支援“singleton”切面bean,不能在xml中組合多個命名連接配接點的聲明

    除此之外,@aspectj切面還有一個優點就是能被spring aop和aspectj同時支援,如果有一天我們需要将應用改為aspectj來實作aop,使用@aspectj将非常容易遷移。

    在spring的配置檔案中,所有的切面、切點和增強處理都必須定義在<aop:config../>元素内部。<beans../>元素可以包含多個<aop:config../>元素,一個<aop:config../>可以包含pointcut、advisor和aspect元素,且這三個元素需要按照此順序來定義。

    注意:當我們使用<aop:config../>方式進行配置時,可能與spring的自動代理方式互相沖突,是以,建議要麼全部使用<aop:config../>配置方式,要麼全部使用自動代理方式,不要把兩者混合使用。

配置切面

    配置<aop:config../>元素時,實質是将已有的spring bean轉換成切面bean,是以需要先定義一個普通的spring bean。因為切面bean可以當成一個普通的spring bean來配置,是以我們完全可以為該切面bean配置依賴注入。當切面bean的定義完成後,通過<aop:congig../>元素中是喲個ref屬性來引用該bean,就可以将該bean轉換成切面bean了。配置<aop:config../>元素時可以指定如下三個屬性:

id:該切面bean的辨別名

ref:指定将要被轉換成切面bean的的普通bean的id

order:指定該切面bean的優先級,值越小,優先級越高

    如下配置片段定義了一個切面:

<a href="http://my.oschina.net/itblog/blog/212056#">?</a>

1

2

3

4

5

6

7

8

<code>&lt;!-- 定義普通的bean執行個體 --&gt;</code>

<code>&lt;</code><code>bean</code> <code>id</code><code>=</code><code>"afteradvicebean"</code> <code>class</code><code>=</code><code>"com.abc.advice.afteradvicebean"</code> <code>/&gt;</code>

<code>&lt;</code><code>aop:config</code><code>&gt;</code>

<code>    </code><code>&lt;!-- 将容器中的afteradvicebean轉換成切面bean --&gt;</code>

<code>    </code><code>&lt;</code><code>aop:aspect</code> <code>id</code><code>=</code><code>"afteradviceaspect"</code> <code>ref</code><code>=</code><code>"afteradvicebean"</code><code>&gt;</code>

<code>        </code><code>...</code>

<code>    </code><code>&lt;/</code><code>aop:aspect</code><code>&gt;</code>

<code>&lt;/</code><code>aop:config</code><code>&gt;</code>

    上面的配置中,将一個afteradvicebean類型普通的bean對象afteradvicebean轉換成了切面bean對象afteradviceaspect。

配置增強處理

    與使用@aspectj完全一樣,使用xml一樣可以配置before、after、afterreturning、afterthrowing和around 5種增強處理,而且完全支援和@aspect完全一樣的語義。使用xml配置增強處理分别依賴于如下幾個元素:

&lt;aop:before../&gt;:配置before增強處理

&lt;aop:after../&gt;:配置after增強處理

&lt;aop:after-returning../&gt;:配置afterreturning增強處理

&lt;aop:after-throwing../&gt;:配置afterthrowing增強處理

&lt;aop:around../&gt;:配置around增強處理

    這些元素都不支援使用子元素,但通常可以指定如下屬性:

pointcut:指定一個切入點表達式,spring将在比對該表達式的連接配接點織入增強處理

pointcut-ref:指定一個已經存在的切入點名稱,通常pointcut和pointcut-ref隻需使用其中之一

method:指定一個方法名,指定切面bean的該方法作為增強處理

throwing:隻對&lt;aop:after-throwing../&gt;元素有效,用于指定一個形參名,afterthrowing增強處理方法,可通過該形參通路目标方法所抛出的異常

returning:隻對&lt;aop:after-returning../&gt;元素有效,用于指定一個形參名,afterthrowing增強處理方法,可通過該形參通路目标方法的傳回值

    既然選擇xml配置檔案的方式來管理切面、切點和增強處理,那麼切面類裡定義切面,切點和增強處理的注解就可以全部删除了。

    定義切點時,xml配置方式和@aspectj注解方式支援完全相同的切點訓示符,一樣可以支援execution、within、args、this、target和bean等切點提示符。另外,xml配置檔案方式也和@aspectj方式一樣支援組合切入點表達式,但xml配置方式不再使用簡單的&amp;&amp;、|| 和 ! 作為組合運算符(因為直接在xml檔案中需要使用實體引用來表示他們),而是使用如下三個組合運算符:and(相當于&amp;&amp;)、or(相當于||)和not(相當于!)。 下面是一個使用&lt;aop:congig../&gt;的例子,這是把前面的例子中關于切面切點和增強處理的注解去掉後,使用xml配置檔案來重新實作這些切面切點的功能:

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

<code>&lt;</code><code>bean</code> <code>id</code><code>=</code><code>"advicetest"</code> <code>class</code><code>=</code><code>"com.abc.advice.advicetest"</code> <code>/&gt;</code>

<code>    </code><code>&lt;!-- 注意這裡可以使用order屬性為aspect指定優先級 --&gt;</code>

<code>    </code><code>&lt;</code><code>aop:aspect</code> <code>id</code><code>=</code><code>"firstaspect"</code> <code>ref</code><code>=</code><code>"advicetest"</code> <code>order</code><code>=</code><code>"2"</code><code>&gt;</code>

<code>    </code> 

<code>        </code><code>&lt;!-- @before切點 --&gt;</code>

<code>        </code><code>&lt;</code><code>aop:before</code> <code>pointcut</code><code>=</code><code>"execution(* com.abc.service.*.*(..))"</code> 

<code>                </code><code>method</code><code>=</code><code>"permissioncheck"</code><code>/&gt;</code>

<code>                </code> 

<code>        </code><code>&lt;!-- @after切點 --&gt;</code>

<code>        </code><code>&lt;</code><code>aop:after</code> <code>pointcut</code><code>=</code><code>"execution(* com.abc.service.*.*(..))"</code> 

<code>                </code><code>method</code><code>=</code><code>"releaseresource"</code><code>/&gt;</code>

<code>        </code><code>&lt;!-- @afterreturning切點 --&gt;</code>

<code>        </code><code>&lt;</code><code>aop:after-returning</code> <code>pointcut</code><code>=</code><code>"execution(* com.abc.service.*.*(..))"</code> 

<code>                </code><code>method</code><code>=</code><code>"log"</code><code>/&gt;</code>

<code>        </code><code>&lt;!-- @afterthrowing切點 --&gt;</code>

<code>        </code><code>&lt;</code><code>aop:after-throwing</code> <code>pointcut</code><code>=</code><code>"execution(* com.abc.service.*.*(..))"</code> 

<code>                </code><code>method</code><code>=</code><code>"handleexception"</code><code>/&gt;</code>

<code>        </code><code>&lt;!-- @around切點(多個切點提示符使用and、or或者not連接配接) --&gt;</code>

<code>        </code><code>&lt;</code><code>aop:around</code> <code>pointcut</code><code>=</code><code>"execution(* com.abc.service.*.*(..)) and args(name,time,..)"</code> 

<code>                </code><code>method</code><code>=</code><code>"process"</code><code>/&gt;</code>

    上面的定義中,特意為firstaspec指定了order=2,表明firstaspect的優先級為2,如果這個xml檔案中還有order=1的aspect,那麼這個aspect将被spring aop優先織入。其執行結果,和前面幾篇文章中介紹的相同,這裡不再給出。

配置切點

    在spring中通過&lt;aop:pointcut../&gt;元素來定義切點。當把&lt;aop:pointcut../&gt;元素作為&lt;aop:config../&gt;的子元素時,表明該切點可以被多個切面共享;當把&lt;aop:pointcut../&gt;元素作為&lt;aop:aspect../&gt;的子元素時,表明該切點隻能在這個切面内使用。配置&lt;aop:pointcut../&gt;時,通常需要配置如下兩個屬性:

id:指定該切點的辨別名

expression:指定該切點關聯的切點表達式

    如下的配置定義了一個簡單的切點:

<code>&lt;aop:pointcut id=</code><code>"point1"</code> <code>expression=</code><code>"execution(* com.abc.service.*.*(..))"</code> <code>/&gt;</code>

    另外,如果程式中已經使用注解的方式定義了切點,在&lt;aop:pointcut../&gt;元素中指定切入點表達式時還有另一種用法,看例子:

<code>&lt;</code><code>aop:pointcut</code> <code>id</code><code>=</code><code>"point2"</code> <code>expression</code><code>=</code><code>"com.abc.service.advicetest.mypointcut()"</code> <code>/&gt;</code>

    下面的程式中定義了一個afterthrowing增強處理,包含該增強處理的切面類如下:

<code>package</code> <code>com.abc.advice;</code>

<code>public</code> <code>class</code> <code>afterthrowingadvicetest {</code>

<code>    </code><code>//定義一個普通方法作為增強處理方法,這個方法名将在xml配置檔案中指定</code>

<code>    </code><code>public</code> <code>void</code> <code>dorecoveryaction(throwable th) {</code>

<code>        </code><code>system.out.println(</code><code>"目标方法抛出異常:"</code> <code>+ th);</code>

<code>        </code><code>system.out.println(</code><code>"模拟資料庫事務恢複"</code><code>);</code>

<code>    </code><code>}</code>

<code>}</code>

    與前面的切面類完全類似,該java類就是一個普通的java類。下面的配置檔案将負責配置該bean執行個體,并将該bean轉換成切面bean:

<code>&lt;</code><code>bean</code> <code>id</code><code>=</code><code>"afterthrowingadvicetest"</code> 

<code>    </code><code>class</code><code>=</code><code>"com.abc.advice.afterthrowingadvicetest"</code> <code>/&gt;</code>

<code>    </code><code>&lt;!-- 這個切點将可以被多個&lt;aop:aspect../&gt;使用 --&gt;</code>

<code>    </code><code>&lt;</code><code>aop:pointcut</code> <code>id</code><code>=</code><code>"mypointcut"</code> 

<code>        </code><code>expression</code><code>=</code><code>"execution(* com.abc.service.*.*(..))"</code> <code>/&gt;</code>

<code>    </code><code>&lt;!-- 這個aspect由上面的bean afterthrowingadvicetest轉化而來 --&gt;</code>

<code>    </code><code>&lt;</code><code>aop:aspect</code> <code>id</code><code>=</code><code>"aspect1"</code> <code>ref</code><code>=</code><code>"afterthrowingadvicetest"</code><code>&gt;</code>

<code>        </code><code>&lt;!-- 定義一個afterthrowing增強處理,指定切入點以切面bean中</code>

<code>            </code><code>的dorecoverryaction作為增強處理方法 --&gt;</code>

<code>        </code><code>&lt;</code><code>aop:after-throwing</code> <code>pointcut-ref</code><code>=</code><code>"mypointcut"</code> 

<code>            </code><code>method</code><code>=</code><code>"dorecoveryaction"</code> <code>throwing</code><code>=</code><code>"th"</code> <code>/&gt;</code>

    上面的&lt;aop:pointcut../&gt;元素定義了一個全局的切點mypointcut,這樣其他切面bean就可以多次複用這個切點了。&lt;aop:after-throwing../&gt;元素中,使用pointcut-ref屬性指定了一個已經存在的切點。