The IoC Container 5. Spring Advice詳解

Advice的幾種定義方式

Before Advice
在切點(diǎn)方法執(zhí)行之前肆汹。

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class BeforeExample {

    @Before("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
    public void doAccessCheck() {
        // ...
    }

}

Advice支持in-place pointcut expression,直接將PointCut的表達(dá)式寫(xiě)在Advice注解里:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class BeforeExample {

    @Before("execution(* com.xyz.myapp.dao.*.*(..))")
    public void doAccessCheck() {
        // ...
    }

}

After Returning Advice
在切點(diǎn)方法正常返回后執(zhí)行笆环。注意:一定是正常返回后。

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterReturning;

@Aspect
public class AfterReturningExample {

    @AfterReturning("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
    public void doAccessCheck() {
        // ...
    }

}

支持綁定切點(diǎn)方法的返回值,returning后變量名需要跟doAccessCheck中的參數(shù)名保持一致闪萄,同時(shí)烈拒,retVal的類型還用來(lái)限制過(guò)濾切點(diǎn)的條件,切點(diǎn)處的方法必須匹配相應(yīng)的返回值才算符合條件:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterReturning;

@Aspect
public class AfterReturningExample {

    @AfterReturning(
        pointcut="com.xyz.myapp.SystemArchitecture.dataAccessOperation()",
        returning="retVal")
    public void doAccessCheck(Object retVal) {
        // ...
    }

}

After Throwing Advice
在切點(diǎn)方法拋出異常之后執(zhí)行祖秒。

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing;

@Aspect
public class AfterThrowingExample {

    @AfterThrowing("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
    public void doRecoveryActions() {
        // ...
    }

}

支持綁定異常到參數(shù)。異常可以是具體異常,用來(lái)作為切點(diǎn)過(guò)濾條件脖阵。throwing后面的參數(shù)名稱需要跟doRecoveryActions的參數(shù)名稱一致。

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing;

@Aspect
public class AfterThrowingExample {

    @AfterThrowing(
        pointcut="com.xyz.myapp.SystemArchitecture.dataAccessOperation()",
        throwing="ex")
    public void doRecoveryActions(DataAccessException ex) {
        // ...
    }

}

After (Finally) Advice
跟java的finally的意義一樣搜立,不管切點(diǎn)處的方法正常返回還是拋出異常,都一定會(huì)在之后執(zhí)行的邏輯。適合用于釋放資源等顿锰。

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.After;

@Aspect
public class AfterFinallyExample {

    @After("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
    public void doReleaseLock() {
        // ...
    }

}

Around Advice
在切點(diǎn)方法執(zhí)行之前和之后執(zhí)行工作谨垃,并確定方法何時(shí)、如何、甚至是否真正執(zhí)行牢撼。

Always use the least powerful form of advice that meets your requirements (that is, do not use around advice if before advice would do).

Around advice is often used if you need to share state before and after a method execution in a thread-safe manner (starting and stopping a timer, for example)

@Around注釋的方法通過(guò)ProceedingJoinPoint pjp參數(shù)來(lái)執(zhí)行被代理的切點(diǎn)方法。pjp.proceed()執(zhí)行的就是切點(diǎn)方法,pjp.proceed()還有個(gè)重載方法是pjp.proceed(Object[] objects)胳喷,可以通過(guò)使用后面的重載方法給切點(diǎn)方法傳入?yún)?shù)尊惰,傳入的參數(shù)會(huì)覆蓋掉原參數(shù)(注意讲竿,這里傳遞的參數(shù)使用Full AspectJ和Spring AOP的語(yǔ)義是不同的泥兰,不過(guò)可以通過(guò)后面講解綁定參數(shù)的方式做到兼容)。doBasicProfiling的返回值最終作為切點(diǎn)方法的返回值返回迈嘹。所以削彬,通過(guò)@Around注解是有能力篡改切點(diǎn)方法的輸入?yún)?shù)和返回值的。

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.ProceedingJoinPoint;

@Aspect
public class AroundExample {

    @Around("com.xyz.myapp.SystemArchitecture.businessService()")
    public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
        // start stopwatch
        Object retVal = pjp.proceed();
        // stop stopwatch
        return retVal;
    }

}

總之秀仲,因?yàn)榍悬c(diǎn)方法是通過(guò)ProceedingJoinPoint參數(shù)調(diào)用的融痛,所以@Around可以完全控制切點(diǎn)方法的訪問(wèn),甚至可以在某些條件滿足的情況下不調(diào)用切點(diǎn)方法也是完全支持的神僵。

Advice Parameters
Access to the Current JoinPoint
所有的Advice方法都可以通過(guò)傳入org.aspectj.lang.JoinPoint參數(shù)來(lái)通過(guò)以下方法訪問(wèn)切點(diǎn)的具體信息(@Around必須傳入ProceedingJoinPoint雁刷,ProceedingJoinPoint是JoinPoint子類):

  • getArgs(): Returns the method arguments.
  • getThis(): Returns the proxy object.
  • getTarget(): Returns the target object.
  • getSignature(): Returns a description of the method that is being advised.
  • toString(): Prints a useful description of the method being advised.

Passing Parameters to Advice
在前面已經(jīng)講解了如何綁定返回值和異常對(duì)象。下面的實(shí)例給出如何綁定切點(diǎn)方法的參數(shù)挑豌,將參數(shù)傳遞給Advice方法:

@Before("com.xyz.myapp.SystemArchitecture.dataAccessOperation() && args(account,..)")
public void validateAccount(Account account) {
    // ...
}

args(account,..)在這里有兩個(gè)用途安券。第一墩崩,限制匹配到的切點(diǎn)方法至少包含一個(gè)參數(shù)氓英,并且參數(shù)類型為Account;第二鹦筹,切點(diǎn)方法的Account參數(shù)對(duì)象可以傳遞給validateAccount使用铝阐。
與上面等價(jià)的定義方式如下:

@Pointcut("com.xyz.myapp.SystemArchitecture.dataAccessOperation() && args(account,..)")
private void accountDataAccessOperation(Account account) {}

@Before("accountDataAccessOperation(account)")
public void validateAccount(Account account) {
    // ...
}

this,target铐拐,annotations (@within, @target, @annotation, and @args)也可以用來(lái)綁定參數(shù)徘键。

The proxy object ( this), target object ( target), and annotations ( @within, @target, @annotation, and @args) can all be bound in a similar fashion.

定義Auditable注解:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Auditable {
    AuditCode value();
}

示例中,用來(lái)匹配使用Auditable注解的公共方法作為切點(diǎn)遍蟋,同時(shí)將注解綁定至參數(shù)吹害。

@Before("com.xyz.lib.Pointcuts.anyPublicMethod() && @annotation(auditable)")
public void audit(Auditable auditable) {
    AuditCode code = auditable.value();
    // ...
}

Advice Parameters and Generics
Spring AOP支持泛型類和泛型參數(shù)的方法。
假設(shè)有以下接口:

public interface Sample<T> {
    void sampleGenericMethod(T param);
    void sampleGenericCollectionMethod(Collection<T> param);
}

以下示例用來(lái)匹配上面Sample<T>接口中定義的泛型方法和綁定泛型參數(shù):

@Before("execution(* ..Sample+.sampleGenericMethod(*)) && args(param)")
public void beforeSampleMethod(MyType param) {
    // Advice implementation
}

但是對(duì)于集合來(lái)說(shuō)虚青,以下的切點(diǎn)定義是不允許的:

@Before("execution(* ..Sample+.sampleGenericCollectionMethod(*)) && args(param)")
public void beforeSampleMethod(Collection<MyType> param) {
    // Advice implementation
}

如果非要使用集合它呀,那么可以定義Collection<?>,由用戶自己來(lái)針對(duì)集合中的類型進(jìn)行檢查和轉(zhuǎn)換棒厘。

Proceeding with Arguments

@Around("execution(List<Account> find*(..)) && " +
        "com.xyz.myapp.SystemArchitecture.inDataAccessLayer() && " +
        "args(accountHolderNamePattern)")
public Object preProcessQueryPattern(ProceedingJoinPoint pjp,
        String accountHolderNamePattern) throws Throwable {
    String newPattern = preProcess(accountHolderNamePattern);
    return pjp.proceed(new Object[] {newPattern});
}

Advice Ordering
多個(gè)Advice的優(yōu)先級(jí)設(shè)置以及需要注意的地方纵穿。

This is done in the normal Spring way by either implementing the org.springframework.core.Ordered interface in the aspect class or annotating it with the Order annotation. Given two aspects, the aspect returning the lower value from Ordered.getValue() (or the annotation value) has the higher precedence.

When two pieces of advice defined in the same aspect both need to run at the same join point, the ordering is undefined

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市奢人,隨后出現(xiàn)的幾起案子谓媒,更是在濱河造成了極大的恐慌,老刑警劉巖何乎,帶你破解...
    沈念sama閱讀 216,692評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件句惯,死亡現(xiàn)場(chǎng)離奇詭異土辩,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)抢野,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門脯燃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人蒙保,你說(shuō)我怎么就攤上這事辕棚。” “怎么了邓厕?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,995評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵逝嚎,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我详恼,道長(zhǎng)补君,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,223評(píng)論 1 292
  • 正文 為了忘掉前任昧互,我火速辦了婚禮挽铁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘敞掘。我一直安慰自己叽掘,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,245評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布玖雁。 她就那樣靜靜地躺著更扁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪赫冬。 梳的紋絲不亂的頭發(fā)上浓镜,一...
    開(kāi)封第一講書(shū)人閱讀 51,208評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音劲厌,去河邊找鬼膛薛。 笑死,一個(gè)胖子當(dāng)著我的面吹牛补鼻,可吹牛的內(nèi)容都是我干的哄啄。 我是一名探鬼主播,決...
    沈念sama閱讀 40,091評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼辽幌,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼增淹!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起乌企,我...
    開(kāi)封第一講書(shū)人閱讀 38,929評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤虑润,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后加酵,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體拳喻,經(jīng)...
    沈念sama閱讀 45,346評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡哭当,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,570評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了冗澈。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片钦勘。...
    茶點(diǎn)故事閱讀 39,739評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖亚亲,靈堂內(nèi)的尸體忽然破棺而出彻采,到底是詐尸還是另有隱情,我是刑警寧澤捌归,帶...
    沈念sama閱讀 35,437評(píng)論 5 344
  • 正文 年R本政府宣布肛响,位于F島的核電站,受9級(jí)特大地震影響惜索,放射性物質(zhì)發(fā)生泄漏特笋。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,037評(píng)論 3 326
  • 文/蒙蒙 一巾兆、第九天 我趴在偏房一處隱蔽的房頂上張望猎物。 院中可真熱鬧,春花似錦角塑、人聲如沸蔫磨。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,677評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)质帅。三九已至,卻和暖如春留攒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背嫉嘀。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,833評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工炼邀, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人剪侮。 一個(gè)月前我還...
    沈念sama閱讀 47,760評(píng)論 2 369
  • 正文 我出身青樓拭宁,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親瓣俯。 傳聞我的和親對(duì)象是個(gè)殘疾皇子杰标,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,647評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容