SpringAOP中的通知Advice解析
一、概述
AOP 中的通知是基于連接點(diǎn)(Join point)業(yè)務(wù)邏輯的一種增強(qiáng),Spring AOP 提供了下面五種通知類型:
- Before advice(前置通知):連接點(diǎn)前面執(zhí)行,不能終止后續(xù)流程,除非拋異常
- After returning advice(后置通知):連接點(diǎn)正常返回時(shí)執(zhí)行,有異常不執(zhí)行
- Around advice(環(huán)繞通知):圍繞連接點(diǎn)前后執(zhí)行,也能捕獲異常處理
- After advice(最終通知):連接點(diǎn)退出時(shí)執(zhí)行,無論是正常退出還是異常退出
- After throwing advice(異常通知):連接點(diǎn)方法拋出異常時(shí)執(zhí)行
AOP 的連接點(diǎn)一般是指目標(biāo)類的方法,五種通知類型執(zhí)行的節(jié)點(diǎn)如下:
二、通知的定義
Spring AOP 可以基于 XML 方式和基于注解方式定義,只是寫法不同,這里只使用注解的方式來講解通知的詳細(xì)用法。
1. 前置通知
在 @Aspect 切面類中使用 @Before 注解簡(jiǎn)單地定義一個(gè)前置通知。
@Aspect @Component public class DemoAspect { @Before("execution(* cn.codeartist.spring.aop.advice.*.*(..))") public void doBefore() { // 自定義邏輯 } }
2. 后置通知
方法正常返回,會(huì)執(zhí)行后置通知,使用 @AfterReturning 注解定義后置通知。
@AfterReturning("execution(* cn.codeartist.spring.aop.advice.*.*(..))") public void doAfterReturning() { // 自定義邏輯 }
注解的 returning 屬性可以綁定目標(biāo)方法返回值,用于在通知中獲取目標(biāo)方法執(zhí)行完成后的返回結(jié)果。
@AfterReturning(pointcut = "pointcut()", returning = "retVal") public void doAfterReturning(Object retVal) { // 自定義邏輯(通過參數(shù)綁定方法切入點(diǎn)方法的返回值) }
當(dāng)注解使用了 returning 屬性時(shí),切入點(diǎn)會(huì)增加返回值類型的限制,上面使用的 Object 類型可以匹配到所有返回值類型的目標(biāo)方法。
例如下面的情況,后置通知的代碼不會(huì)被執(zhí)行:
// 目標(biāo)方法 public String doService() { return "碼匠公眾號(hào)"; } // 后置通知定義 @AfterReturning(pointcut = "pointcut()", returning = "retVal") public void doAfterReturning(Integer retVal) { // 目標(biāo)方法返回值是String類型,結(jié)果參數(shù)綁定類型是Integer,該通知不會(huì)被執(zhí)行 }
3. 環(huán)繞通知
環(huán)繞通知可以在方法執(zhí)行的任何節(jié)點(diǎn)添加邏輯,它可以實(shí)現(xiàn)另外 4 種通知的功能。
如果需要以線程安全的方式在方法執(zhí)行前后共享狀態(tài),可以使用環(huán)繞通知。
用 @Around 注解來定義環(huán)繞通知,需要使用 ProceedingJoinPoint 作為參數(shù),來執(zhí)行目標(biāo)方法調(diào)用。
@Around("execution(* cn.codeartist.spring.aop.advice.*.*(..))") public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable { // 方法執(zhí)行前邏輯 Object retVal = joinPoint.proceed(); // 方法執(zhí)行后邏輯 return retVal; }
joinPoint.proceed() 會(huì)調(diào)用目標(biāo)方法,或者是調(diào)用另一個(gè)切面。
雖然環(huán)繞通知可以實(shí)現(xiàn)另外幾種通知的功能,但在使用中都能實(shí)現(xiàn)功能的情況下,優(yōu)先使用其他通知方式。
4. 最終通知
最終通知在方法退出的時(shí)候執(zhí)行,使用 @After 注解定義,最終通知在方法正常退出和拋出異常時(shí)都會(huì)執(zhí)行,通常用于資源的釋放。
@After("execution(* cn.codeartist.spring.aop.advice.*.*(..))") public void doAfter() { // 自定義邏輯 }
5. 異常通知
方法拋出異常的時(shí)候會(huì)執(zhí)行異常通知,使用 @AfterThrowing 定義異常通知。
@AfterThrowing("execution(* cn.codeartist.spring.aop.advice.*.*(..))") public void doAfterThrowing() { // 自定義邏輯 }
注解的 throwing 屬性用來綁定目標(biāo)方法拋出的異常,用于在通知中獲取目標(biāo)方法拋出的異常實(shí)例。
@AfterThrowing(pointcut = "pointcut()", throwing = "ex") public void doAfterThrowing(Throwable ex) { // 自定義邏輯(通過參數(shù)綁定方法切入點(diǎn)方法拋出的異常) }
當(dāng)注解使用了 throwing 屬性時(shí),切入點(diǎn)會(huì)增加異常類型的限制,上面使用的 Throwable 類型可以匹配到所有異常類型。
例如下面的情況,異常通知的代碼不會(huì)被執(zhí)行:
// 目標(biāo)方法 public void doServiceThrow() { throw new RuntimeException("Test exception"); } // 異常通知定義 @AfterThrowing(pointcut = "pointcut()", throwing = "ex") public void doAfterThrowing(NullPointerException ex) { // 目標(biāo)方法拋出的是RuntimeException,參數(shù)綁定類型是NullPointerException,該通知不會(huì)被執(zhí)行 }
三、通知的參數(shù)
在定義通知的方法簽名上可以指定參數(shù)來綁定目標(biāo)方法的一些信息(例如前面講到的后置通知和異常通知)。
1. 切入點(diǎn)
在定義通知方法的時(shí)候,一般可以使用 JoinPoint 作為參數(shù),環(huán)繞通知使用 ProceedingJoinPoint 。常用接口方法如下:
JoinPoint
public interface JoinPoint { // 獲取代理對(duì)象 Object getThis(); // 獲取目標(biāo)對(duì)象 Object getTarget(); // 獲取連接點(diǎn)方法的參數(shù) Object[] getArgs(); // 獲取連接點(diǎn)方法的簽名 Signature getSignature(); }
ProceedingJoinPoint
public interface ProceedingJoinPoint extends JoinPoint { // 執(zhí)行下一個(gè)切面的通知或者目標(biāo)方法 public Object proceed() throws Throwable; // 執(zhí)行下一個(gè)切面的通知或者目標(biāo)方法(帶參數(shù)) public Object proceed(Object[] args) throws Throwable; }
切面的切入點(diǎn)一般為方法,所以 Signature 可以轉(zhuǎn)換為 MethodSignature 使用。
2. 通知的參數(shù)傳遞
通知的參數(shù)可以通過切點(diǎn)表達(dá)式 args 來指定,具體用法會(huì)在后面切點(diǎn)表達(dá)式詳解中講到。
@Before("pointcut() && args(name,..)") public void doBefore(String name) { // 切點(diǎn)表達(dá)式增加參數(shù)匹配 }
args(name,..) 也會(huì)增加切入點(diǎn)的限制,目標(biāo)方法的參數(shù)個(gè)數(shù)至少為一個(gè),且第一個(gè)參數(shù)類型為 String 。
四、通知的順序
Spring AOP 中一個(gè)目標(biāo)類可以被多個(gè)切面切入,多個(gè)切面也可以切入一個(gè)目標(biāo)類。
使用 @Order 注解來指定切面的優(yōu)先級(jí),來控制切面的執(zhí)行順序。
在注冊(cè)切面 Bean 的時(shí)候指定 @Order ,如下:
@Order(1) @Aspect @Component public class FirstAspect { // ...... }
優(yōu)先級(jí)高的切面先執(zhí)行,通知執(zhí)行的順序如下:
可以得出:
- 優(yōu)先級(jí)高的切面,前置通知先執(zhí)行
- 優(yōu)先級(jí)低的切面,后置通知先執(zhí)行
- Order 值越小,優(yōu)先級(jí)越大。
Spring AOP 是基于動(dòng)態(tài)代理的攔截器模式實(shí)現(xiàn)的,切面模型與攔截器模型相似,如下:
五、附錄
1. 常用注解
注解 | 描述 |
@Aspect | 定義切面類 |
@Before | 定義前置通知 |
@AfterReturning | 定義后置通知 |
@Around | 定義環(huán)繞通知 |
@After | 定義最終通知 |
@AfterThrowing | 定義異常通知 |
到此這篇關(guān)于SpringAOP中的通知Advice解析的文章就介紹到這了,更多相關(guān)通知Advice解析內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot?jackson提供對(duì)LocalDate的支持方式
這篇文章主要介紹了SpringBoot?jackson提供對(duì)LocalDate的支持方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01SpringSecurity實(shí)現(xiàn)動(dòng)態(tài)權(quán)限校驗(yàn)的過程
Spring Security過濾器鏈中,AuthorizationFilter的authorizationManager是我們要找的組件,該組件的check方法已被棄用,推薦使用authorize方法,最終通過接口路徑和權(quán)限進(jìn)行校驗(yàn),本文給大家介紹SpringSecurity實(shí)現(xiàn)動(dòng)態(tài)權(quán)限校驗(yàn)的相關(guān)知識(shí),感興趣的朋友一起看看吧2025-02-02詳解SpringBoot之訪問靜態(tài)資源(webapp...)
這篇文章主要介紹了詳解SpringBoot之訪問靜態(tài)資源(webapp...),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09java代碼抓取網(wǎng)頁郵箱的實(shí)現(xiàn)方法
下面小編就為大家?guī)硪黄猨ava代碼抓取網(wǎng)頁郵箱的實(shí)現(xiàn)方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-06-06java虛擬機(jī)jvm方法區(qū)實(shí)例講解
在本篇文章里小編給大家整理的是一篇關(guān)于java虛擬機(jī)jvm方法區(qū)實(shí)例講解內(nèi)容,有興趣的朋友們可以學(xué)習(xí)下。2021-02-02Java超詳細(xì)講解設(shè)計(jì)模式中的命令模式
命令模式是將一個(gè)請(qǐng)求封裝為一個(gè)對(duì)象,從而可用不同的請(qǐng)求對(duì)客戶進(jìn)行參數(shù)化,對(duì)請(qǐng)求排隊(duì)或者對(duì)請(qǐng)求做日志記錄,以及可以支持撤銷的操作2022-04-04SpringBoot 整合 Avro 與 Kafka的詳細(xì)過程
本文介紹了如何在Spring Boot中使用Avro和Kafka進(jìn)行數(shù)據(jù)的序列化和反序列化,并通過MyBatisPlus將數(shù)據(jù)存入數(shù)據(jù)庫,感興趣的朋友跟隨小編一起看看吧2024-12-12詳解Spring/Spring boot異步任務(wù)編程WebAsyncTask
這篇文章主要介紹了詳解Spring/Spring boot異步任務(wù)編程WebAsyncTask,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-06-06SpringBoot JVM參數(shù)調(diào)優(yōu)方式
這篇文章主要介紹了SpringBoot JVM參數(shù)調(diào)優(yōu)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09