除了前面介紹的基于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><!-- 定義普通的bean執行個體 --></code>
<code><</code><code>bean</code> <code>id</code><code>=</code><code>"afteradvicebean"</code> <code>class</code><code>=</code><code>"com.abc.advice.afteradvicebean"</code> <code>/></code>
<code><</code><code>aop:config</code><code>></code>
<code> </code><code><!-- 将容器中的afteradvicebean轉換成切面bean --></code>
<code> </code><code><</code><code>aop:aspect</code> <code>id</code><code>=</code><code>"afteradviceaspect"</code> <code>ref</code><code>=</code><code>"afteradvicebean"</code><code>></code>
<code> </code><code>...</code>
<code> </code><code></</code><code>aop:aspect</code><code>></code>
<code></</code><code>aop:config</code><code>></code>
上面的配置中,将一個afteradvicebean類型普通的bean對象afteradvicebean轉換成了切面bean對象afteradviceaspect。
配置增強處理
與使用@aspectj完全一樣,使用xml一樣可以配置before、after、afterreturning、afterthrowing和around 5種增強處理,而且完全支援和@aspect完全一樣的語義。使用xml配置增強處理分别依賴于如下幾個元素:
<aop:before../>:配置before增強處理
<aop:after../>:配置after增強處理
<aop:after-returning../>:配置afterreturning增強處理
<aop:after-throwing../>:配置afterthrowing增強處理
<aop:around../>:配置around增強處理
這些元素都不支援使用子元素,但通常可以指定如下屬性:
pointcut:指定一個切入點表達式,spring将在比對該表達式的連接配接點織入增強處理
pointcut-ref:指定一個已經存在的切入點名稱,通常pointcut和pointcut-ref隻需使用其中之一
method:指定一個方法名,指定切面bean的該方法作為增強處理
throwing:隻對<aop:after-throwing../>元素有效,用于指定一個形參名,afterthrowing增強處理方法,可通過該形參通路目标方法所抛出的異常
returning:隻對<aop:after-returning../>元素有效,用于指定一個形參名,afterthrowing增強處理方法,可通過該形參通路目标方法的傳回值
既然選擇xml配置檔案的方式來管理切面、切點和增強處理,那麼切面類裡定義切面,切點和增強處理的注解就可以全部删除了。
定義切點時,xml配置方式和@aspectj注解方式支援完全相同的切點訓示符,一樣可以支援execution、within、args、this、target和bean等切點提示符。另外,xml配置檔案方式也和@aspectj方式一樣支援組合切入點表達式,但xml配置方式不再使用簡單的&&、|| 和 ! 作為組合運算符(因為直接在xml檔案中需要使用實體引用來表示他們),而是使用如下三個組合運算符:and(相當于&&)、or(相當于||)和not(相當于!)。 下面是一個使用<aop:congig../>的例子,這是把前面的例子中關于切面切點和增強處理的注解去掉後,使用xml配置檔案來重新實作這些切面切點的功能:
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<code><</code><code>bean</code> <code>id</code><code>=</code><code>"advicetest"</code> <code>class</code><code>=</code><code>"com.abc.advice.advicetest"</code> <code>/></code>
<code> </code><code><!-- 注意這裡可以使用order屬性為aspect指定優先級 --></code>
<code> </code><code><</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>></code>
<code> </code>
<code> </code><code><!-- @before切點 --></code>
<code> </code><code><</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>/></code>
<code> </code>
<code> </code><code><!-- @after切點 --></code>
<code> </code><code><</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>/></code>
<code> </code><code><!-- @afterreturning切點 --></code>
<code> </code><code><</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>/></code>
<code> </code><code><!-- @afterthrowing切點 --></code>
<code> </code><code><</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>/></code>
<code> </code><code><!-- @around切點(多個切點提示符使用and、or或者not連接配接) --></code>
<code> </code><code><</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>/></code>
上面的定義中,特意為firstaspec指定了order=2,表明firstaspect的優先級為2,如果這個xml檔案中還有order=1的aspect,那麼這個aspect将被spring aop優先織入。其執行結果,和前面幾篇文章中介紹的相同,這裡不再給出。
配置切點
在spring中通過<aop:pointcut../>元素來定義切點。當把<aop:pointcut../>元素作為<aop:config../>的子元素時,表明該切點可以被多個切面共享;當把<aop:pointcut../>元素作為<aop:aspect../>的子元素時,表明該切點隻能在這個切面内使用。配置<aop:pointcut../>時,通常需要配置如下兩個屬性:
id:指定該切點的辨別名
expression:指定該切點關聯的切點表達式
如下的配置定義了一個簡單的切點:
<code><aop:pointcut id=</code><code>"point1"</code> <code>expression=</code><code>"execution(* com.abc.service.*.*(..))"</code> <code>/></code>
另外,如果程式中已經使用注解的方式定義了切點,在<aop:pointcut../>元素中指定切入點表達式時還有另一種用法,看例子:
<code><</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>/></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><</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>/></code>
<code> </code><code><!-- 這個切點将可以被多個<aop:aspect../>使用 --></code>
<code> </code><code><</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>/></code>
<code> </code><code><!-- 這個aspect由上面的bean afterthrowingadvicetest轉化而來 --></code>
<code> </code><code><</code><code>aop:aspect</code> <code>id</code><code>=</code><code>"aspect1"</code> <code>ref</code><code>=</code><code>"afterthrowingadvicetest"</code><code>></code>
<code> </code><code><!-- 定義一個afterthrowing增強處理,指定切入點以切面bean中</code>
<code> </code><code>的dorecoverryaction作為增強處理方法 --></code>
<code> </code><code><</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>/></code>
上面的<aop:pointcut../>元素定義了一個全局的切點mypointcut,這樣其他切面bean就可以多次複用這個切點了。<aop:after-throwing../>元素中,使用pointcut-ref屬性指定了一個已經存在的切點。