Spring @Around注解使用及說(shuō)明
@Around是 Spring AOP(面向切面編程)中的一個(gè)注解,它用于定義一個(gè)環(huán)繞通知(Around Advice)。
環(huán)繞通知是 AOP 中最強(qiáng)大的一種通知類(lèi)型,因?yàn)樗軌蛟诜椒▓?zhí)行之前和之后都執(zhí)行自定義的邏輯,并且可以控制方法是否繼續(xù)執(zhí)行或改變其返回值。
@Around注解的基本用法
要使用@Around注解,你需要先定義一個(gè)切面(Aspect),然后在該切面中使用@Around注解來(lái)標(biāo)注一個(gè)方法,該方法將作為環(huán)繞通知。環(huán)繞通知方法必須接受一個(gè)ProceedingJoinPoint類(lèi)型的參數(shù),這個(gè)參數(shù)提供了對(duì)正在執(zhí)行的方法的訪(fǎng)問(wèn)。
以下是一個(gè)簡(jiǎn)單的例子:
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class MyAroundAdvice {
@Around("execution(* com.example.service.*.*(..))")
public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
// 在方法執(zhí)行之前的邏輯
System.out.println("Before method execution");
try {
Object result = pjp.proceed(); // 執(zhí)行目標(biāo)方法
// 在方法執(zhí)行之后的邏輯
System.out.println("After method execution");
return result; // 返回目標(biāo)方法的執(zhí)行結(jié)果
} catch (Throwable throwable) {
// 處理異常
System.out.println("Exception occurred: " + throwable.getMessage());
throw throwable; // 重新拋出異常
}
}
}
@Around注解的工作機(jī)制
匹配切入點(diǎn):
@Around注解的值是一個(gè)切入點(diǎn)表達(dá)式(Pointcut Expression),它指定了哪些方法將被這個(gè)環(huán)繞通知攔截。在上面的例子中,切入點(diǎn)表達(dá)式"execution(* com.example.service.*.*(..))"匹配了com.example.service包下所有類(lèi)的所有方法。創(chuàng)建代理:Spring AOP 會(huì)在運(yùn)行時(shí)為匹配到的方法創(chuàng)建代理對(duì)象。當(dāng)這些方法被調(diào)用時(shí),實(shí)際上調(diào)用的是代理對(duì)象的方法。
執(zhí)行環(huán)繞通知:當(dāng)代理對(duì)象的方法被調(diào)用時(shí),Spring AOP 會(huì)先執(zhí)行環(huán)繞通知中的邏輯。在環(huán)繞通知中,你可以通過(guò)調(diào)用**
ProceedingJoinPoint的proceed**方法來(lái)執(zhí)行目標(biāo)方法。你也可以選擇不調(diào)用proceed方法,從而阻止目標(biāo)方法的執(zhí)行。處理返回值和異常:環(huán)繞通知可以獲取目標(biāo)方法的返回值,并在必要時(shí)進(jìn)行修改。同時(shí),它也可以捕獲目標(biāo)方法拋出的異常,并進(jìn)行相應(yīng)的處理。
以下是一些常見(jiàn)的@Around注解應(yīng)用場(chǎng)景:
日志記錄:在目標(biāo)方法執(zhí)行之前和之后記錄日志,可以幫助開(kāi)發(fā)者了解方法的執(zhí)行流程、輸入?yún)?shù)、返回值以及執(zhí)行時(shí)間等信息。這對(duì)于調(diào)試和監(jiān)控應(yīng)用程序的狀態(tài)非常有幫助。
性能監(jiān)控:通過(guò)在目標(biāo)方法執(zhí)行前后記錄時(shí)間戳,可以計(jì)算出方法的執(zhí)行時(shí)間。這對(duì)于性能優(yōu)化和瓶頸識(shí)別非常重要,可以幫助開(kāi)發(fā)者找出需要優(yōu)化的代碼段。
事務(wù)管理:在目標(biāo)方法執(zhí)行之前開(kāi)啟一個(gè)事務(wù),并在方法執(zhí)行成功后提交事務(wù);如果方法執(zhí)行失敗,則回滾事務(wù)。這樣可以確保數(shù)據(jù)的一致性和完整性。
緩存管理:在目標(biāo)方法執(zhí)行之前檢查緩存中是否存在所需的數(shù)據(jù);如果存在,則直接返回緩存中的數(shù)據(jù),避免重復(fù)執(zhí)行耗時(shí)的方法;如果不存在,則執(zhí)行方法并將結(jié)果存入緩存中。
權(quán)限檢查:在目標(biāo)方法執(zhí)行之前檢查當(dāng)前用戶(hù)是否具有執(zhí)行該方法的權(quán)限;如果沒(méi)有權(quán)限,則拋出異?;蚍祷劐e(cuò)誤提示,從而保護(hù)系統(tǒng)的安全性。
異常處理:捕獲目標(biāo)方法拋出的異常,并進(jìn)行統(tǒng)一處理,如記錄異常日志、發(fā)送報(bào)警通知或返回友好的錯(cuò)誤提示給用戶(hù)。這可以提高系統(tǒng)的健壯性和用戶(hù)體驗(yàn)。
輸入?yún)?shù)校驗(yàn):在目標(biāo)方法執(zhí)行之前對(duì)輸入?yún)?shù)進(jìn)行校驗(yàn),確保參數(shù)的有效性和合法性。如果參數(shù)無(wú)效或不合法,則拋出異?;蚍祷劐e(cuò)誤提示,避免執(zhí)行無(wú)效的操作。
結(jié)果后處理:在目標(biāo)方法執(zhí)行之后對(duì)返回結(jié)果進(jìn)行后處理,如格式化數(shù)據(jù)、轉(zhuǎn)換數(shù)據(jù)類(lèi)型或添加額外的信息。這可以滿(mǎn)足不同的業(yè)務(wù)需求和數(shù)據(jù)展示要求。
綜上所述,@Around注解在Spring AOP中具有廣泛的應(yīng)用場(chǎng)景,它能夠幫助開(kāi)發(fā)者在不修改目標(biāo)方法代碼的情況下實(shí)現(xiàn)各種復(fù)雜的功能和邏輯。通過(guò)合理地使用@Around注解,可以提高代碼的可讀性、可維護(hù)性和可擴(kuò)展性。
@Around 注解常用場(chǎng)景實(shí)例
權(quán)限校驗(yàn)
以下是一個(gè)使用@Around注解進(jìn)行權(quán)限校驗(yàn)的示例代碼:
@Around("@annotation(authCheck)")
public Object doInterceptor(ProceedingJoinPoint joinPoint, AuthCheck authCheck) throws Throwable {
String mustRole = authCheck.mustRole();
RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
User loginUser = userService.getLoginUser(request);
if (!loginUser.hasRole(mustRole)) {
throw new AccessDeniedException("用戶(hù)沒(méi)有權(quán)限執(zhí)行此操作");
}
return joinPoint.proceed(); // 繼續(xù)執(zhí)行目標(biāo)方法
}
在這個(gè)示例中,doInterceptor方法會(huì)在被@AuthCheck注解標(biāo)記的方法執(zhí)行前后運(yùn)行。首先檢查用戶(hù)是否有足夠的權(quán)限,如果沒(méi)有權(quán)限則拋出異常,否則繼續(xù)執(zhí)行目標(biāo)方法。
日志記錄和性能監(jiān)控
以下是一個(gè)使用@Around注解的具體實(shí)例,該實(shí)例展示了如何在Spring AOP中實(shí)現(xiàn)日志記錄和性能監(jiān)控的功能。
首先,我們需要定義一個(gè)切面類(lèi),并在其中使用@Around注解來(lái)標(biāo)注環(huán)繞通知方法。
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LoggingAndPerformanceAspect {
@Around("execution(* com.example.service.*.*(..))")
public Object logAndMeasureExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
// 記錄方法開(kāi)始執(zhí)行的時(shí)間
long startTime = System.currentTimeMillis();
// 記錄日志:方法開(kāi)始執(zhí)行
System.out.println("Method " + joinPoint.getSignature().getName() + " is starting with arguments: " + Arrays.toString(joinPoint.getArgs()));
try {
// 執(zhí)行目標(biāo)方法
Object result = joinPoint.proceed();
// 記錄方法執(zhí)行結(jié)束的時(shí)間
long endTime = System.currentTimeMillis();
// 記錄日志:方法執(zhí)行結(jié)束,并輸出執(zhí)行時(shí)間
System.out.println("Method " + joinPoint.getSignature().getName() + " executed in " + (endTime - startTime) + " milliseconds with result: " + result);
// 返回目標(biāo)方法的執(zhí)行結(jié)果
return result;
} catch (Throwable throwable) {
// 記錄異常日志
System.out.println("Exception occurred in method " + joinPoint.getSignature().getName() + ": " + throwable.getMessage());
// 重新拋出異常,以便上層調(diào)用者處理
throw throwable;
}
}
}
在這個(gè)例子中,LoggingAndPerformanceAspect是一個(gè)切面類(lèi),它包含了一個(gè)使用@Around注解標(biāo)注的環(huán)繞通知方法logAndMeasureExecutionTime。這個(gè)方法會(huì)在匹配到的方法執(zhí)行之前和之后分別記錄日志,并測(cè)量方法的執(zhí)行時(shí)間。
接下來(lái),我們需要確保Spring容器能夠識(shí)別這個(gè)切面類(lèi),并將其應(yīng)用到相應(yīng)的目標(biāo)方法上。通常,這可以通過(guò)在Spring配置文件中啟用AOP自動(dòng)代理或者通過(guò)@EnableAspectJAutoProxy注解來(lái)實(shí)現(xiàn)。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
// 其他Bean定義...
// 確保LoggingAndPerformanceAspect被Spring容器管理
@Bean
public LoggingAndPerformanceAspect loggingAndPerformanceAspect() {
return new LoggingAndPerformanceAspect();
}
}
現(xiàn)在,當(dāng)Spring容器中的任何匹配到切入點(diǎn)表達(dá)式"execution(* com.example.service.*.*(..))"的方法被執(zhí)行時(shí),logAndMeasureExecutionTime環(huán)繞通知都會(huì)被觸發(fā),從而記錄日志和測(cè)量執(zhí)行時(shí)間。
例如,如果你有一個(gè)服務(wù)類(lèi)MyService,其中有一個(gè)方法doSomething,那么當(dāng)這個(gè)方法被調(diào)用時(shí),你會(huì)在控制臺(tái)看到類(lèi)似以下的輸出:
Method doSomething is starting with arguments: [...] Method doSomething executed in XXX milliseconds with result: ...
這樣,你就成功地使用@Around注解實(shí)現(xiàn)了日志記錄和性能監(jiān)控的功能。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Mybatis-plus配置之日期時(shí)間自動(dòng)填充實(shí)踐
本文介紹如何使用MyBatis-Plus的MetaObjectHandler接口實(shí)現(xiàn)新增和更新時(shí)間的自動(dòng)填充,通過(guò)繼承抽象類(lèi)、添加注解及處理版本兼容性,簡(jiǎn)化開(kāi)發(fā)流程并減少手動(dòng)操作2025-08-08
關(guān)于SpringBoot單元測(cè)試(cobertura生成覆蓋率報(bào)告)
這篇文章主要介紹了關(guān)于SpringBoot單元測(cè)試(cobertura生成覆蓋率報(bào)告),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11
MyBatis批量插入(insert)數(shù)據(jù)操作
本文給大家分享MyBatis批量插入(insert)數(shù)據(jù)操作知識(shí),非常不錯(cuò),具有參考借鑒價(jià)值,感興趣的朋友一起學(xué)習(xí)吧2016-06-06
Mybatis 返回值類(lèi)型和參數(shù)傳遞的配置方法
在 MyBatis 中,返回值類(lèi)型和參數(shù)傳遞是 Mapper 接口中至關(guān)重要的兩個(gè)方面,正確理解和使用它們可以幫助我們高效、準(zhǔn)確地進(jìn)行數(shù)據(jù)庫(kù)操作,接下來(lái)通過(guò)本文給大家介紹Mybatis 返回值類(lèi)型和參數(shù)傳遞的配置方法,感興趣的朋友跟隨小編一起看看吧2024-08-08
在SpringBoot項(xiàng)目中整合攔截器的詳細(xì)步驟
在系統(tǒng)中經(jīng)常需要在處理用戶(hù)請(qǐng)求之前和之后執(zhí)行一些行為,例如檢測(cè)用戶(hù)的權(quán)限,或者將請(qǐng)求的信息記錄到日志中,即平時(shí)所說(shuō)的"權(quán)限檢測(cè)"及"日志記錄",下面這篇文章主要給大家介紹了關(guān)于在SpringBoot項(xiàng)目中整合攔截器的相關(guān)資料,需要的朋友可以參考下2022-09-09
java+vue3+el-tree實(shí)現(xiàn)樹(shù)形結(jié)構(gòu)操作代碼
基于springboot + vue3 elementPlus實(shí)現(xiàn)樹(shù)形結(jié)構(gòu)數(shù)據(jù)的添加、刪除和頁(yè)面展示,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2024-06-06
java 高并發(fā)中volatile的實(shí)現(xiàn)原理
這篇文章主要介紹了java 高并發(fā)中volatile的實(shí)現(xiàn)原理的相關(guān)資料,在多線(xiàn)程并發(fā)編程中synchronized和Volatile都扮演著重要的角色,Volatile是輕量級(jí)的synchronized,它在多處理器開(kāi)發(fā)中保證了共享變量的“可見(jiàn)性”,需要的朋友可以參考下2017-03-03

