Spring學習手冊(10)—— Spring AOP配置

Spring學習手冊(9)—— Spring AOP入門講述了AOP技術以及AOP基本概念,最后我們了解了Spring對AOP的支持。本文我們將以XML配置的方式來學習Spring AOP的具體使用。

一、引入aop模式

如果想使用XML的方式配置AOP信息,我們需要先在XML配置文件中引入aop模式(aop schema),因此我們的XML配置文件頭頭信息如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> 
        <!-- bean definitions here -->
</beans>

這樣配之后,我們就可以在xml文件里面直接引用aop標簽了。

二、定義一個切面

一個Spring AOP的切面(Aspect)在xml中也是一個傳統的bean,而使用標簽<aop:aspect>定義一個切面,并且使用ref來執行被定義為該切面的bean。

<aop:config>
    <aop:aspect id="myAspect" ref="aBean">
        ...
    </aop:aspect>
</aop:config>

<bean id="aBean" class="...">
    ...
</bean>

如上,我們定義了一個id為aBean的bean實例,然后使用<aop:aspect>標簽定義了一個id為myAspect的切面,而該切面指向aBean。

三、定義一個切點(pointcut)

上篇我們說過,切點表達式用于匹配連接點(join point),然后根據配置的增強(Advice)方法在連接點運行時選擇合適時間執行。因為Spring AOP目前僅支持運行方法類型的連接點,所以也可以認為與一個切點匹配一個bean的執行方法。

一個切點包含兩部分:

  • 包含方法和名字的簽名;
  • 切點表達式:用于匹配具體的方法

因此我們使用xml定義切點如下所示:

<aop:config>
    <aop:aspect id="myAspect" ref="aBean">
        <aop:pointcut id="businessService"
            expression="execution(* com.xyz.myapp.service.*.*(..))"/>
        ...
    </aop:aspect>
</aop:config>

<aop:pointcut>必須在<aop:aspect>內部使用定義切點,使用expression的值指明切點表達式,id為該切點定義類唯一標示方便配置引用。這里并沒有定義切點(pointcut) 簽名,切點簽名定義一般在使用@AspectJ注解方式定義切點時定義。下面我們著重說明下切點表達式的語法情況。

支持的切點(pointcut)標示

Spring AOP 支持以下AspectJ 的切點標示(AspectJ pointcut designators)簡稱PCD,由于Spring AOP并沒有全部支持所有的PCD,因此若使用了不存在該列表內的標示則會拋出異常。

標簽名 說明
execution 匹配運行方法的連接點
within 使匹配連接點限定在特定類型
this 限定匹配的連接點是給定類型的實例
target 限定匹配連接點的目標是給定類型的實例
args 限定連接點的參數是給定類型的實例
@target 限定匹配的連接點的運行的對象有該類型的注解
@args 運行時傳遞的參數擁有給定類型的注解
@within 限定匹配給定指定注解類型的連接點
@annotation 限定連接點擁有指定的注解
bean 使得連接點匹配特質的bean或bean集合

Tip:我們可以使用&&||來將連接點表達式關聯起來。
然而在XML配置中該類字符需要轉換,因此我們可以使用更語義化的句子符號andornot

例子

使用execution例子

 execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)
        throws-pattern?)

以上除ret-type-patternname-patternparam-pattern為必須外,其他皆為可選。

  • 匹配所有的公有方法
    execution(public * *(..))
  • 匹配所有以set開頭的方法
    execution(* set*(..))
  • 匹配所有AccountService接口定義的方法
    execution(* com.xyz.service.AccountService.*(..))
  • 匹配所有service包內的方法
    execution(* com.xyz.service..(..))
  • 匹配所有service包以及子包內的方法
    execution(* com.xyz.service...(..))
  • 所有在service包內的連接點
    within(com.xyz.service.*)
  • 所有service包以及子包內的連接點
    within(com.xyz.service..*)
  • 所有實現AccountService接口的連接點
    this(com.xyz.service.AccountService)
  • 所有的目標對象實現AccountService接口的連接點
    target(com.xyz.service.AccountService)
  • 有一個參數并且運行時傳入參數為Serializable的連接點
    args(java.io.Serializable)
  • 目標對象有@Transactional注解
    @target(org.springframework.transaction.annotation.Transactional)
  • 定義的目標對象類型含有@Transactional注解
    @within(org.springframework.transaction.annotation.Transactional)
  • 含有@Transactional注解的可運行方法
    @annotation(org.springframework.transaction.annotation.Transactional)
  • 含有一個參數并且在運行時傳入的參數含有@Classified注解
    @args(com.xyz.security.Classified)
  • bean名字定義為tradeService的連接點
    bean(tradeService)
  • bean名字滿足Service命名的所有連接點
    bean(
    Service)

四、定義增強方法(Advice)

Before advice
<aop:aspect id="beforeExample" ref="aBean">
    <aop:pointcut id="dataAccessOperation"
            expression="execution(* com.xyz.myapp.dao.*.*(..))"/>
    
    <aop:before
        pointcut-ref="dataAccessOperation"
        method="doAccessCheck"/>

    ...

</aop:aspect>

<aop:before>使用pointcut-ref引用一個切點,并且指定增強執行方法為doAccessCheck。值的注意的是名為aBean的bean(也就是我們定義的切面)必須實現doAccessCheck方法。該方法會在目標方法執行前執行。

After returning advice
<aop:aspect id="afterReturningExample" ref="aBean">
    <aop:pointcut id="dataAccessOperation"
            expression="execution(* com.xyz.myapp.dao.*.*(..))"/>
    <aop:after-returning
        pointcut-ref="dataAccessOperation"
        method="doAccessCheck"/>

    ...
</aop:aspect>

同上,該方法會在切點執行正常返回后執行。
當然如果你需要獲取返回對象的話,你需要將配置信息改為如下所示:

<aop:aspect id="afterReturningExample" ref="aBean">
    ...
    <aop:after-returning
        pointcut-ref="dataAccessOperation"
        returning="retVal"
        method="doAccessCheck"/>

    ...

</aop:aspect>

其中doAccessCheck方法定義如下,其中參數名必須與XML里面配置相同,也就是說必須與XML中的retVal一致。

public void doAccessCheck(Object retVal) {...}
After throwing advice
<aop:aspect id="afterThrowingExample" ref="aBean">
    ...
    <aop:after-throwing
        pointcut-ref="dataAccessOperation"
        method="doRecoveryActions"/>

    ...

</aop:aspect>

當然如果需要獲取拋出的異常時我們可以如下配置:

<aop:aspect id="afterThrowingExample" ref="aBean">

    <aop:after-throwing
        pointcut-ref="dataAccessOperation"
        throwing="dataAccessEx"
        method="doRecoveryActions"/>

    ...

</aop:aspect>

其中doRecoveryActions方法定義如下且必須有名為dataAccessEx參數:

public void doRecoveryActions(DataAccessException dataAccessEx) {...}
After (finally) advice

切點返回后執行如下增強方法(無論正常返回還是異常退出):

<aop:aspect id="afterFinallyExample" ref="aBean">

    <aop:after
        pointcut-ref="dataAccessOperation"
        method="doReleaseLock"/>

    ...

</aop:aspect>
Around advice 環繞增強方法

該類型的Advice環繞著連接點,該增強方法可以選擇調用或者不掉用連接點方法。

<aop:aspect id="aroundExample" ref="aBean">

    <aop:around
        pointcut-ref="businessService"
        method="doBasicProfiling"/>

    ...

</aop:aspect>
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
    // start stopwatch
    Object retVal = pjp.proceed();
    // stop stopwatch
    return retVal;
}

如上所示,我們使用aop:around來定義環繞增強,方法定義的第一個參數必須為ProceedingJoinPoint類,在Around增強方法中,需要調用processed方法才會執行真正的切點方法,否則則會放棄執行切點方法。

Advice 參數

有時我們需要獲取到目標方法的參數信息,而Spring AOP為我們提供了方便的獲取方式,接下來我們就來了解下如何通過XML配置來獲取目標方法的參數信息。
我們定義如下代碼

package com.aop.learn.service;

public interface StudentQueryService {

    /**
     * 根據姓名和年齡查詢學生信息,
     * 假設無重復現象
    */
    Student  queryStdent(String name,int age);
    
    //...
}
package com.aop.learn.service.impl;
public class StudentQueryServiceImpl implements StudentQueryService {
    public Student queryStdent(String name, int age) {

        // do something 
        // query database or create a new object 
    }
}

切面例子(攔截器)

package com.aop.learn.interceptor;
public class InterceptorSample {

    public Object interceptorMethod(ProceedingJoinPoint call, String name, int age) throws Throwable {
          // do something
          Object result =  call.proceed();
        //do something
    }
}

配置信息如下

<!--定義bean信息-->
<bean id="aspectSample",class="com.aop.learn.interceptor.InterceptorSample">
<!--配置切面信息-->
<aop:config>
        <aop:aspect ref="aspectSample">

            <aop:pointcut id="pointsample"
                expression="execution(* com.aop.learn.service.StudentQueryService.queryStdent(..))
                and args(name, age)"/>
            <aop:around pointcut-ref="pointsample"
                method="interceptorMethod"/>
        </aop:aspect>
    </aop:config>

如上配置信息,在expression語句中增加了args(name,age),該表達式定義了變量名為name和age的參數,該參數名必須和增強方法中名字一致,這樣我們就能在增強方法中獲取目標方法的參數信息了。

五、總結

本文我們主要學習了如何使用XML配置方式來完成Spring AOP的使用。文章中我們學習了很多配置標簽,完成了切面定義、切點定義以及多種增強方法的定義。至此我們已經掌握了Spring AOP的基本知識點,可在具體的項目開發中使用。不過學了那么多的知識點,我們還是需要一定的實踐來進行消化吸收。接下來我們將構造例子用來鞏固學習到的知識點。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,333評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,491評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,263評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,946評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,708評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,409評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,939評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,774評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,641評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,872評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,650評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容