Spring AOP用法

參考:

spring aop中pointcut表達(dá)式完整版 - 知乎

我用一張圖徹底了解 SpringAOP 切面表達(dá)式陳浩的博客-CSDN博客切面表達(dá)式

原來理解 AOP 可以這么簡單! - 騰訊云開發(fā)者社區(qū)-騰訊云

AOP參數(shù)詳解_碼農(nóng)小胖哥的技術(shù)博客_51CTO博客

Spring AOP(切面編程)

AOP (Aspect Orient Programming),直譯過來就是 面向切面編程尉姨。AOP 是一種編程思想逐哈,是面向?qū)ο缶幊蹋∣OP)的一種補(bǔ)充。AOP的目的是實(shí)現(xiàn)關(guān)注點(diǎn)的分離便贵;

==切點(diǎn)只能是spring bean的方法菠镇。==

術(shù)語、概念

通知(advice)

AOP框架中的增強(qiáng)處理承璃。通知描述了切面何時執(zhí)行以及如何執(zhí)行增強(qiáng)處理

連接點(diǎn)(join point)

連接點(diǎn)表示應(yīng)用執(zhí)行過程中能夠插入切面的一個點(diǎn)利耍,這個點(diǎn)可以是方法的調(diào)用,異常的拋出。在Spring AOP中隘梨,連接點(diǎn)總是方法的調(diào)用程癌。

切點(diǎn)(PointCut)

可以插入增強(qiáng)處理的連接點(diǎn)。

切面(Aspect)

切面是通知和切點(diǎn)的結(jié)合轴猎。

引入(Introduction)

引入允許我們向現(xiàn)有的類添加新的方法或者屬性席楚。

織入(Weaving)

將增強(qiáng)處理添加到目標(biāo)對象中,并創(chuàng)建一個被增強(qiáng)的對象税稼,這個過程就是織入

9種切點(diǎn)表達(dá)式(@Pointcut)

說明

不重要的說明:

引用其他命名切入點(diǎn)烦秩,只有@ApectJ風(fēng)格(注解)支持,Schema風(fēng)格(XML配置)不支持郎仆,本文展示均為@ApectJ風(fēng)格只祠。

切點(diǎn)表達(dá)式簡單解析

合并切入點(diǎn)表達(dá)式:

切入點(diǎn)表達(dá)式可以使用&&||!來合并.還可以通過名字來指向切入點(diǎn)表達(dá)式扰肌∨浊蓿【也可以用于類型和參數(shù)中】

類型匹配模式:

1:*:匹配任意類型的單個字符;比如模式 (*,String) 匹配了一個接受兩個參數(shù)的方法曙旭,第一個可以是任意類型盗舰,第二個則必須是String類型

2:..:匹配任何數(shù)量字符的重復(fù),如在類型模式中匹配任何數(shù)量子包桂躏;而在方法參數(shù)模式中匹配任何數(shù)量參數(shù)钻趋,可以是零到多個。

3:+:匹配指定類型及子類型(包含當(dāng)前類型)剂习;僅能作為后綴放在類型后邊蛮位。

萬能:

execution是最靈活最常用的切點(diǎn)表達(dá)式。所有的表達(dá)式都可以用execution表達(dá)式表示

指定類

within=>this=>target

相同點(diǎn):都是指定至類的鳞绕。

不同點(diǎn):

within失仁,路徑可使用通配符,可指定接口们何,可指定類萄焦。

this,路徑不可使用通配符冤竹!可指定接口拂封,不可指定類!

target贴见,路徑不可使用通配符烘苹!不可指定接口!可指定類片部。

指定參數(shù)類型

args,匹配當(dāng)前執(zhí)行的方法傳入的參數(shù)為指定類型的執(zhí)行方法(是方法==傳入==的參數(shù)類型镣衡,不是方法==聲明==的參數(shù)類型)霜定。

參數(shù)類型的路徑不可使用通配符。

args屬于動態(tài)切入點(diǎn)廊鸥,這種切入點(diǎn)開銷非常大望浩,非特殊情況最好不要使用

跟注解有關(guān)的

@annotation=>@target=>@within=>@args

@args匹配方法有持有某注解參數(shù)。依然是動態(tài)切入點(diǎn)不建議使用

@annotation匹配持有某注解的方法惰说。

@within@target

相同點(diǎn):都是匹配所有持有指定注解類型內(nèi)的方法磨德。

不同點(diǎn):

@within,必須是在目標(biāo)對象上聲明這個注解吆视,在接口上聲明的對它不起作用

@target典挑,必須是在目標(biāo)對象上聲明這個注解,在接口上聲明的對它不起作用
(測試中啦吧,用@target會報(bào)錯您觉,大概意思是aop太寬泛不可以使用)

具體區(qū)別可見(02-05 AOP學(xué)習(xí)之@within和@target使用示例及對比分析 - 簡書)

bean匹配bean名。

execution

匹配方法執(zhí)行的連接點(diǎn)授滓,這是你將會用到的Spring的最主要的切入點(diǎn)指定者琳水。

@注解只能使用全限定名,不能模糊使用..般堆,例如:@java.lang.Deprecated * *(..)可以在孝,@java..Deprecated * *(..)不行

// 匹配該包下的所有類的所有方法(不包含子包)
@Pointcut("execution(* com.example.demo.util.*.*(..))")
// 匹配該包及子包下所有類的所有方法
@Pointcut("execution(* com.example.demo.util..*.*(..))")
// 匹配該包下public修飾的所有參數(shù)為(Integer,Integer)的方法
@Pointcut("execution(public * com.example.demo.util.*.*(Integer,Integer))")
// 匹配該包及子包下所有類的所有a開頭的方法
@Pointcut("execution(* com.example.demo.util..a*(..))")
// 匹配該包下IDataServer接口中的任何方法
@Pointcut("execution(* com.example.demo.service.IDataServer.*(..))")
// 匹配該包及子包下IDataServer接口中的任何方法(可用于接口,所有調(diào)用接口的方法都可以被織入)
@Pointcut("execution(* com.example.demo..IDataServer.*(..))")
// 與或非示例淮摔,可用于各種位置
@Pointcut("execution(void||Integer com.example.demo..IDataServer.*())")
// 組合條件示例:IDataServer里的所有void方法 并且 com及其子包下IDataServer的add方法
@Pointcut("execution(void com.example.demo.service.IDataServer.*(..)) && execution(* com..IDataServer.add(..))")
// 匹配該包及子包下IDataServer接口及子類型的的任何方法
@Pointcut("execution(* com.example.demo..DataImpl+.*(..))")// + 包含當(dāng)前類型
@Pointcut("execution(* com.example.demo..IDataServer+.*(..))")

/*支持注解*/
// 任何持有AopAnno注解的方法
@Pointcut("execution(@com.example.demo.aop.AopAnno * *(..))")
// 任何持有AopAnno注解 和 Deprecated注解的方法(并且的關(guān)系)
@Pointcut("execution(@com.example.demo.aop.AopAnno @java.lang.Deprecated * *(..))")
// 任何持有AopAnno注解 或 Deprecated注解的方法(或者的關(guān)系)
@Pointcut("execution(@(com.example.demo.aop.AopAnno || java.lang.Deprecated) * *(..))")

// 匹配返回值是Integer的帶有@Deprecated注解的所有方法
@Pointcut("execution(@java.lang.Deprecated Integer *(..))")
// 匹配所有返回值持有@Data的方法 (@lombok.Data *)是帶有@Data的對象作為返回值
@Pointcut("execution((@lombok.Data *) *(..))")
// 匹配任何帶有一個參數(shù)的方法私沮,且該參數(shù)類型持有@Data的方法 (@lombok.Data *)是帶有@Data的對象作為返回值
@Pointcut("execution(* *(@lombok.Data *))")
// 匹配任何參數(shù)帶有兩個參數(shù)的方法,且這個兩個參數(shù)都被@Data標(biāo)記了(這里展示了參數(shù)的兩種寫法噩咪,帶括號和不帶括號)
@Pointcut("execution(* *(@lombok.Data *,@lombok.Data (*)))")

// 演示示例:多參數(shù)多注解寫法:這里展示了參數(shù)的兩種寫法顾彰,帶括號和不帶括號.這里展示了多個注解關(guān)系的寫法,或者和并且
@Pointcut("execution(* *(@(lombok.Getter || lombok.Setter) *,@lombok.Getter @lombok.Setter (*),@(lombok.Getter&&lombok.Setter) (*)))")

/*支持泛型胃碾,泛型支持注解、+號*/
// 匹配任何參數(shù)帶有List<DataModel>的方法
@Pointcut("execution(* *(java.util.List<com..DataModel>))")
// 匹配任何參數(shù)帶有List<DataModel及子類型>的方法
@Pointcut("execution(* *(java.util.List<com..DataModel+>))")
// 匹配任何參數(shù)帶有List<持有@Data注解的DataModel及子類型>的方法
@Pointcut("execution(* *(java.util.List<@lombok.Data com..DataModel+>))")

// throws 不管用筋搏,這依然會匹配throws RunTimeException異常的方法,反之一樣仆百。
@Pointcut("execution(* com.example.demo.util.DemoBean.*(..)) throws java.io.IOException")

within

限定匹配特定類型的連接點(diǎn)(在使用SpringAOP的時候,在匹配的類型中定義的方法的執(zhí)行)奔脐。

/*針對的類和接口*/
// 匹配該包下所有類的所有方法的執(zhí)行
@Pointcut("within(com.example.demo..*)")
// 匹配該包及子包下的IDataServer內(nèi)所有方法的執(zhí)行
@Pointcut("within(com.example.demo..IDataServer+)")
// 匹配所有持有@Deprecated的類所有方法
@Pointcut("within(@java.lang.Deprecated *)")

this

限定匹配特定的連接點(diǎn)(使用Spring AOP的時候方法的執(zhí)行)俄周,其中bean reference(Spring AOP 代理)是指定類型的實(shí)例。

常用于命名綁定模式髓迎。對由代理對象的類型進(jìn)行過濾篩選峦朗。

目標(biāo)對象使用aop之后生成的代理對象必須是指定的類型才會被攔截,注意是目標(biāo)對象被代理之后生成的代理對象和指定的類型匹配才會被攔截

// 支持controller和service(接口)
@Pointcut("this(com.example.demo.controller.TestController)")
@Pointcut("this(com.example.demo.service.IDataServer)")
// 通配符支持:
// 路徑不支持通配符(..排龄、*)
@Pointcut("this(com.example.demo.*.IDataServer)")// 錯誤X波势。不會生效
@Pointcut("this(com.example.demo..IDataServer)")// 錯誤X。不會生效
//  類名接口名支持通配符(*)不支持通配符(+)
@Pointcut("this(com.example.demo.service.IDataS*)")// 支持√
@Pointcut("this(com.example.demo.util.DemoBean+)")// 錯誤X。不會生效

target

限定匹配特定的連接點(diǎn)(使用SpringAOP的時候方法的執(zhí)行)尺铣,其中目標(biāo)對象(被代理的appolication object)是指定類型的實(shí)例拴曲。

// 支持controller,不支持接口
@Pointcut("target(com.example.demo.controller.TestController)")
@Pointcut("target(com.example.demo.service.IDataServer)")// 不支持接口
@Pointcut("target(com.example.demo.service.impl.DataImpl)")// 僅支持bean
// 通配符支持:
// 路徑不支持通配符(..、*)
@Pointcut("target(com.example.demo.service.*.DataImpl)")// 錯誤X凛忿。不會生效
@Pointcut("target(com.example.demo.service..DataImpl)")// 錯誤X澈灼。不會生效
//  類名接口名支持通配符(*)不支持通配符(+)
@Pointcut("target(com.example.demo.service.impl.DataI*)")// 支持√
@Pointcut("target(com.example.demo.util.DemoBean+)")// 錯誤X侄泽。不會生效

args

限定匹配特定的連接點(diǎn)(使用Spring AOP的時候方法的執(zhí)行)痊硕,其中參數(shù)是指定類型的實(shí)例楣颠。

// 匹配第一個參數(shù)是Serializable子類的參數(shù)枣宫,其余參數(shù)0到多個
@Pointcut("args(java.io.Serializable+,..)")

@within

限定匹配特定的連接點(diǎn)司草,其中連接點(diǎn)所在類型已指定注解(在使用Spring AOP的時候彭谁,所執(zhí)行的方法所在類型已指定注解)苫纤。

該注解只能是注解到實(shí)現(xiàn)類上阔籽,注解到service層匹配不到哦

// 匹配持有該注解的類的所有方法
@Pointcut("@within(com.example.demo.aop.AopAnno)")
@Pointcut("@within(org.springframework.stereotype.Service)")
@Pointcut("@within(com.example.demo.*.AopAnno)") // 錯誤X叠赦,僅支持全限定名

@target

限定匹配特定的連接點(diǎn)(使用SpringAOP的時候方法的執(zhí)行)驹马,其中執(zhí)行的對象的類已經(jīng)有指定類型的注解。

表述和@within一樣除秀,但是@within能運(yùn)行起來糯累,@target運(yùn)行不起來,報(bào)錯册踩,所以盡量不考慮使用該表達(dá)式了泳姐。

@args

限定匹配特定的連接點(diǎn)(使用SpringAOP的時候方法的執(zhí)行),其中實(shí)際傳入?yún)?shù)的運(yùn)行時類型有指定類型的注解暂吉。

// 匹配方法有一個參數(shù)且參數(shù)持有@Data注解
@Pointcut("@args(lombok.Data)")
// 匹配方法有多個參數(shù)且第一個參數(shù)持有@Data注解
@Pointcut("@args(lombok.Data,..)")

@annotation

限定匹配特定的連接點(diǎn)(使用SpringAOP的時候方法的執(zhí)行)胖秒,其中連接點(diǎn)的主題有某種給定的注解

// 匹配持有@Deprecated注解的所有方法
@Pointcut("@annotation(java.lang.Deprecated)")

bean(拓展,第10種表達(dá)式)

根據(jù)beanNam來匹配慕的。支持*通配符

// 匹配所有以Controller結(jié)尾的Bean的所有方法
bean(*Controller)

5種通知類型

@Before阎肝、@After、@AfterReturning肮街、@AfterThrowing风题,可選擇聲明JoinPoint參數(shù)。

@Around需要聲明ProceedingJoinPoint參數(shù)嫉父。

前置通知(Before)

在方法執(zhí)行前通知

@Before("execOrder()")
public void doBefore(JoinPoint joinPoint) {
    Console.log("doBefore...");
}

方法執(zhí)行后通知(@After)

在目標(biāo)方法執(zhí)行后無論是否發(fā)生異常沛硅,執(zhí)行通知,不能訪問目標(biāo)方法的執(zhí)行的結(jié)果绕辖。

@After("execOrder()")
public void doAfter(JoinPoint joinPoint) {
    Console.log("doAfter...");
}

環(huán)繞通知(@Around)

可以將要執(zhí)行的方法(point.proceed())進(jìn)行包裹執(zhí)行摇肌,可以在前后添加需要執(zhí)行的操作

/**
* 環(huán)繞
* @param proceedingJoinPoint
* @return
* @throws Throwable
*/
@Around("execOrder()")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
    Console.log("doAround-start...");
    Object proceed = proceedingJoinPoint.proceed();
    Console.log("doAround-end...");
    return proceed;
}

后置通知(@AfterReturning)

在方法正常執(zhí)行完成進(jìn)行通知,可以訪問到方法的返回值的仪际。

@AfterReturning(value = "execOrder()",returning = "a")
public void doAfterReturning(Object a) {
    Console.log("doAfterReturning...");
    System.out.println("返回值:"+a);
}

異常通知(@AfterThrowing)

在方法出現(xiàn)異常時進(jìn)行通知,可以訪問到異常對象围小,且可以指定在出現(xiàn)特定異常時在執(zhí)行通知昵骤。

@AfterThrowing(pointcut="execOrder()",throwing="a")
public void doaction(Throwable a) {
    Console.log("doAfterThrowing...");
    System.out.println("目標(biāo)方法中拋出的異常:"+a);
}

代碼實(shí)現(xiàn)

1.maven中引入依賴:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2.編輯切面類(簡單代碼示例)

@Aspect
@Component
@Slf4j
public class AspectAop {

    /** 換行符 */
    private static final String LINE_SEPARATOR = System.lineSeparator();

    // 定義切點(diǎn):
    @Pointcut("execution(* com.example.demo.controller.*.*(..))")
    public void webLog(){}

    /**
     * 在切點(diǎn)之前織入
     * @param joinPoint
     * @throws Throwable
     */
    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) {
        
    }

    /**
     * 在切點(diǎn)之后織入
     * @throws Throwable
     */
    @After("webLog()")
    public void doAfter() throws Throwable {
        
    }
    /**
     * 環(huán)繞
     * @param proceedingJoinPoint
     * @return
     * @throws Throwable
     */
    @Around("webLog()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        // 環(huán)繞前 doing...
        Object result = proceedingJoinPoint.proceed();
        // 環(huán)繞后 doing...
        return result;
    }
}

執(zhí)行順序問題:

程序正常:

doAround-start...doBefore...RunningMethod......doAfterReturning...doAfter...doAround-end...

程序異常:

doAround-start...doBefore...RunningMethod......doAfterThrowing...doAfter...

總結(jié):


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市吩抓,隨后出現(xiàn)的幾起案子涉茧,更是在濱河造成了極大的恐慌,老刑警劉巖疹娶,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件伴栓,死亡現(xiàn)場離奇詭異,居然都是意外死亡雨饺,警方通過查閱死者的電腦和手機(jī)钳垮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來额港,“玉大人饺窿,你說我怎么就攤上這事∫普叮” “怎么了肚医?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長向瓷。 經(jīng)常有香客問我肠套,道長,這世上最難降的妖魔是什么猖任? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任你稚,我火速辦了婚禮,結(jié)果婚禮上朱躺,老公的妹妹穿的比我還像新娘刁赖。我一直安慰自己,他們只是感情好长搀,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布宇弛。 她就那樣靜靜地躺著,像睡著了一般源请。 火紅的嫁衣襯著肌膚如雪涯肩。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天巢钓,我揣著相機(jī)與錄音,去河邊找鬼疗垛。 笑死症汹,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的贷腕。 我是一名探鬼主播背镇,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼咬展,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了瞒斩?” 一聲冷哼從身側(cè)響起破婆,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎胸囱,沒想到半個月后祷舀,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡烹笔,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年裳扯,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谤职。...
    茶點(diǎn)故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡饰豺,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出允蜈,到底是詐尸還是另有隱情冤吨,我是刑警寧澤,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布饶套,位于F島的核電站漩蟆,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏凤跑。R本人自食惡果不足惜爆安,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望仔引。 院中可真熱鬧扔仓,春花似錦、人聲如沸咖耘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽儿倒。三九已至版保,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間夫否,已是汗流浹背彻犁。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留凰慈,地道東北人汞幢。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像微谓,于是被迫代替她去往敵國和親森篷。 傳聞我的和親對象是個殘疾皇子输钩,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評論 2 353

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