一文詳解Spring Aop @After(后置通知)的使用場(chǎng)景
核心定義
@After 是 Spring AOP 中的另一種通知(Advice)類型,通常被稱為“后置通知”或“最終通知”。
它的核心作用是:
無(wú)論目標(biāo)方法是正常執(zhí)行完成,還是在執(zhí)行過(guò)程中拋出了異常,@After 通知中的代碼 總是 會(huì)在目標(biāo)方法執(zhí)行之后被執(zhí)行。
最經(jīng)典的類比就是 Java 中的 try...catch...finally 語(yǔ)句塊里的 finally 部分。@After 的行為和 finally 塊的行為幾乎一模一樣。
@After 通知的執(zhí)行流程
為了更好地理解,我們來(lái)看兩種情況下的執(zhí)行順序:
情況一:目標(biāo)方法成功執(zhí)行
@Before通知執(zhí)行。- 目標(biāo)方法 (
targetMethod()) 執(zhí)行并正常返回。 @After通知執(zhí)行。- (如果定義了)
@AfterReturning通知執(zhí)行。
情況二:目標(biāo)方法拋出異常
@Before通知執(zhí)行。- 目標(biāo)方法 (
targetMethod()) 執(zhí)行,中途拋出異常。 @After通知執(zhí)行。- (如果定義了)
@AfterThrowing通知執(zhí)行。 - 異常繼續(xù)向上層調(diào)用棧拋出。
一個(gè)非常關(guān)鍵的點(diǎn)是:@After 通知本身無(wú)法訪問(wèn)目標(biāo)方法的返回值(因?yàn)樗赡芨緵](méi)有返回值,比如拋異常時(shí)),也無(wú)法捕獲或處理從目標(biāo)方法中拋出的異常。它只是一個(gè)保證“最后一定會(huì)被執(zhí)行”的鉤子。
@After 通知能做什么?(主要應(yīng)用場(chǎng)景)
后置通知非常適合執(zhí)行那些必須進(jìn)行的“清理”或“收尾”工作,無(wú)論業(yè)務(wù)邏輯成功與否。
資源釋放 (Resource Cleanup)
- 這是
@After最重要、最常見(jiàn)的用途。類似于finally塊。 - 示例:釋放文件句柄、關(guān)閉網(wǎng)絡(luò)連接、關(guān)閉數(shù)據(jù)庫(kù)連接池中的連接等。確保即使業(yè)務(wù)代碼出錯(cuò),關(guān)鍵資源也不會(huì)被泄露。
上下文清理 (Context Cleanup)
- 如果在 @Before 通知中向 ThreadLocal 存放了數(shù)據(jù),那么在 @After 通知中將其 remove() 是一個(gè)最佳實(shí)踐。這可以防止在線程池環(huán)境中發(fā)生內(nèi)存泄漏或數(shù)據(jù)錯(cuò)亂。
最終日志記錄 (Final Auditing)
- 記錄一個(gè)操作的結(jié)束。
- 示例:“方法
updateProduct執(zhí)行完畢。” 這個(gè)日志不關(guān)心成功或失敗,只記錄“結(jié)束”這個(gè)事實(shí)。
性能監(jiān)控 (Performance Monitoring)
- 可以在 @Before 中記錄一個(gè)開(kāi)始時(shí)間,然后在 @After 中記錄結(jié)束時(shí)間,并計(jì)算總耗時(shí)。
示例:
- @Before: long startTime = System.currentTimeMillis(); (存入 ThreadLocal)
- @After: long endTime = System.currentTimeMillis(); long duration = endTime - startTime; log.info("方法耗時(shí): {} ms", duration);
與 @AfterReturning 和 @AfterThrowing 的區(qū)別
這是新手很容易混淆的地方,理解它們的區(qū)別至關(guān)重要:
| 知類型 | 執(zhí)行時(shí)機(jī) | 能否訪問(wèn)返回值? | 能否訪問(wèn)異常? | 主要用途 |
|---|---|---|---|---|
| @After (最終通知) | 總是在目標(biāo)方法后執(zhí)行(無(wú)論成功或失?。?/td> | 不能 | 不能 | 資源清理、最終日志 |
| @AfterReturning (返回通知) | 僅在目標(biāo)方法成功執(zhí)行后執(zhí)行 | 可以 | 不適用 | 基于返回結(jié)果的附加操作 |
| @AfterThrowing (異常通知) | 僅在目標(biāo)方法拋出異常后執(zhí)行 | 不適用 | 可以 | 異常日志記錄、告警通知 |
簡(jiǎn)單來(lái)說(shuō):
- 想總是執(zhí)行清理?用
@After。 - 想在成功后根據(jù)返回值做點(diǎn)事?用
@AfterReturning。 - 想在失敗后專門處理異常?用
@AfterThrowing。
代碼示例
我們擴(kuò)展之前的例子,增加一個(gè)刪除方法(可能會(huì)失?。樗蟹椒ㄌ砑?nbsp;@After 通知。
1. 業(yè)務(wù)服務(wù)類 (目標(biāo)對(duì)象)
package com.example.service;
import org.springframework.stereotype.Service;
@Service
public class UserService {
// 成功執(zhí)行的例子
public String findUserById(Long id) {
System.out.println("--- 核心業(yè)務(wù)邏輯:正在根據(jù) ID 查詢用戶... ---");
return "User" + id;
}
// 拋出異常的例子
public void deleteUser(Long id) {
System.out.println("--- 核心業(yè)務(wù)邏輯:正在嘗試刪除用戶... ---");
if (id <= 0) {
throw new IllegalArgumentException("用戶ID無(wú)效,刪除失敗!");
}
System.out.println("用戶 " + id + " 已被成功刪除。");
}
}
2. 切面類 (Aspect) 中定義 @After 通知
package com.example.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Aspect
@Component
public class LoggingAspect {
@Pointcut("execution(public * com.example.service.*.*(..))")
public void serviceLayerPointcut() {}
// 前置通知
@Before("serviceLayerPointcut()")
public void logBefore(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
System.out.println("==================================================");
System.out.printf("[AOP 前置通知]: 方法 [%s] 即將執(zhí)行... 參數(shù): %s%n", methodName, Arrays.toString(args));
}
// 后置通知 (最終通知)
@After("serviceLayerPointcut()")
public void logAfter(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
System.out.printf("[AOP 后置通知]: 方法 [%s] 執(zhí)行完畢。執(zhí)行清理工作...%n", methodName);
System.out.println("--------------------------------------------------\n");
}
}
3. 運(yùn)行代碼并觀察輸出
調(diào)用成功的方法 userService.findUserById(101L):
================================================== [AOP 前置通知]: 方法 [findUserById] 即將執(zhí)行... 參數(shù): [101] --- 核心業(yè)務(wù)邏輯:正在根據(jù) ID 查詢用戶... --- [AOP 后置通知]: 方法 [findUserById] 執(zhí)行完畢。執(zhí)行清理工作... --------------------------------------------------
@After 在方法成功后執(zhí)行了。
調(diào)用失敗的方法 userService.deleteUser(0L) (需要用 try-catch 捕獲異常):
try {
userService.deleteUser(0L);
} catch (Exception e) {
System.err.println("在調(diào)用方捕獲到異常: " + e.getMessage());
}
輸出:
================================================== [AOP 前置通知]: 方法 [deleteUser] 即將執(zhí)行... 參數(shù): [0] --- 核心業(yè)務(wù)邏輯:正在嘗試刪除用戶... --- [AOP 后置通知]: 方法 [deleteUser] 執(zhí)行完畢。執(zhí)行清理工作... -------------------------------------------------- 在調(diào)用方捕獲到異常: 用戶ID無(wú)效,刪除失??!
即使 deleteUser 拋出了異常,@After 通知 (logAfter 方法) 依然被執(zhí)行了,完美地展示了其 finally 的特性。
總結(jié)
| 特性 | 描述 |
|---|---|
| 執(zhí)行時(shí)機(jī) | 無(wú)論成功或失敗,總是在目標(biāo)方法執(zhí)行之后執(zhí)行。 |
| 核心用途 | 資源釋放、上下文清理、最終日志記錄等收尾工作。 |
| 行為類似 | Java 的 finally 語(yǔ)句塊。 |
| 關(guān)鍵限制 | 無(wú)法訪問(wèn)目標(biāo)方法的返回值,也無(wú)法捕獲或修改異常。 |
| 關(guān)鍵參數(shù) | 可以注入 JoinPoint 對(duì)象,獲取方法元數(shù)據(jù)。 |
以上就是一文詳解Spring Aop @After(后置通知)的使用場(chǎng)景的詳細(xì)內(nèi)容,更多關(guān)于Spring Aop @After使用場(chǎng)景的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java畢業(yè)設(shè)計(jì)實(shí)戰(zhàn)之生活旅行分享平臺(tái)的實(shí)現(xiàn)
這是一個(gè)使用了java+Springboot+JPA+Jsp+Html+js+Ajax+maven+mysql開(kāi)發(fā)的生活旅行分享平臺(tái),是一個(gè)畢業(yè)設(shè)計(jì)的實(shí)戰(zhàn)練習(xí),具有分享發(fā)布平臺(tái)該有的所有功能,感興趣的朋友快來(lái)看看吧2022-02-02
Java語(yǔ)言實(shí)現(xiàn)基數(shù)排序代碼分享
這篇文章主要介紹了Java語(yǔ)言實(shí)現(xiàn)基數(shù)排序代碼分享,具有一定借鑒價(jià)值,需要的朋友可以參考下。2017-12-12
spring boot入門開(kāi)始你的第一個(gè)應(yīng)用
這篇文章主要介紹了spring boot入門開(kāi)始你的第一個(gè)應(yīng)用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,,需要的朋友可以參考下2019-06-06
java利用java.net.URLConnection發(fā)送HTTP請(qǐng)求的方法詳解
如何通過(guò)Java(模擬瀏覽器)發(fā)送HTTP請(qǐng)求是我們?cè)谌粘=?jīng)常會(huì)遇到的問(wèn)題,下面這篇文章主要給大家介紹了關(guān)于java利用java.net.URLConnection發(fā)送HTTP請(qǐng)求的相關(guān)資料,文中介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面來(lái)一起看看吧。2017-05-05
Java去重排序之Comparable與Comparator的使用及說(shuō)明
這篇文章主要介紹了Java去重排序之Comparable與Comparator的使用及說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04
springboot整合騰訊云短信開(kāi)箱即用的示例代碼
這篇文章主要介紹了springboot整合騰訊云短信開(kāi)箱即用,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-03-03
java中靜態(tài)變量和實(shí)例變量的區(qū)別詳細(xì)介紹
本篇文章介紹了,java中靜態(tài)變量和實(shí)例變量的區(qū)別。需要的朋友參考下2013-05-05
Java遞歸來(lái)實(shí)現(xiàn)漢諾塔游戲,注釋詳細(xì)
這篇文章介紹了Java遞歸來(lái)實(shí)現(xiàn)漢諾塔游戲的方法,文中的代碼注釋介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-11-11
解決springboot 多線程使用MultipartFile讀取excel文件內(nèi)容報(bào)錯(cuò)問(wèn)題
這篇文章主要介紹了解決springboot 多線程使用MultipartFile讀取excel文件內(nèi)容報(bào)錯(cuò)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-09-09

