SpringBoot 中用 AOP 實(shí)現(xiàn)一個權(quán)限校驗(yàn)的思路總結(jié)
點(diǎn)擊上方?泥瓦匠 關(guān)注我!
1 理解AOP
1.1 什么是AOP
AOP(Aspect Oriented Programming),面向切面思想,是Spring的三大核心思想之一(兩外兩個:IOC-控制反轉(zhuǎn)、DI-依賴注入)。
那么AOP為何那么重要呢?
在我們的程序中,經(jīng)常存在一些系統(tǒng)性的需求,比如權(quán)限校驗(yàn)、日志記錄、統(tǒng)計(jì)等,這些代碼會散落穿插在各個業(yè)務(wù)邏輯中,非常冗余且不利于維護(hù)。例如下面這個示意圖:
有多少業(yè)務(wù)操作,就要寫多少重復(fù)的校驗(yàn)和日志記錄代碼,這顯然是無法接受的。當(dāng)然,用面向?qū)ο蟮乃枷?,我們可以把這些重復(fù)的代碼抽離出來,寫成公共方法,就是下面這樣:

這樣,代碼冗余和可維護(hù)性的問題得到了解決,但每個業(yè)務(wù)方法中依然要依次手動調(diào)用這些公共方法,也是略顯繁瑣。有沒有更好的方式呢?有的,那就是AOP,AOP將權(quán)限校驗(yàn)、日志記錄等非業(yè)務(wù)代碼完全提取出來,與業(yè)務(wù)代碼分離,并尋找節(jié)點(diǎn)切入業(yè)務(wù)代碼中:

1.2 AOP體系與概念
簡單地去理解,其實(shí)AOP要做三類事:
在哪里切入,也就是權(quán)限校驗(yàn)等非業(yè)務(wù)操作在哪些業(yè)務(wù)代碼中執(zhí)行。
在什么時候切入,是業(yè)務(wù)代碼執(zhí)行前還是執(zhí)行后。
切入后做什么事,比如做權(quán)限校驗(yàn)、日志記錄等。
因此,AOP的體系可以梳理為下圖:
一些概念詳解:
Pointcut:切點(diǎn),決定處理如權(quán)限校驗(yàn)、日志記錄等在何處切入業(yè)務(wù)代碼中(即織入切面)。切點(diǎn)分為execution方式和annotation方式。前者可以用路徑表達(dá)式指定哪些類織入切面,后者可以指定被哪些注解修飾的代碼織入切面。Advice:處理,包括處理時機(jī)和處理內(nèi)容。處理內(nèi)容就是要做什么事,比如校驗(yàn)權(quán)限和記錄日志。處理時機(jī)就是在什么時機(jī)執(zhí)行處理內(nèi)容,分為前置處理(即業(yè)務(wù)代碼執(zhí)行前)、后置處理(業(yè)務(wù)代碼執(zhí)行后)等。Aspect:切面,即Pointcut和Advice。Joint point:連接點(diǎn),是程序執(zhí)行的一個點(diǎn)。例如,一個方法的執(zhí)行或者一個異常的處理。在 Spring AOP 中,一個連接點(diǎn)總是代表一個方法執(zhí)行。Weaving:織入,就是通過動態(tài)代理,在目標(biāo)對象方法中執(zhí)行處理內(nèi)容的過程。
網(wǎng)絡(luò)上有張圖,我覺得非常傳神,貼在這里供大家觀詳:
2 AOP實(shí)例
實(shí)踐出真知,接下來我們就擼代碼來實(shí)現(xiàn)一下AOP。完成項(xiàng)目已上傳至:
https://github.com/ThinkMugz/aopDemo
使用 AOP,首先需要引入 AOP 的依賴。參數(shù)校驗(yàn):這么寫參數(shù)校驗(yàn)(validator)就不會被勸退了~
<dependency>
????<groupId>org.springframework.bootgroupId>
????<artifactId>spring-boot-starter-aopartifactId>
dependency>
2.1 第一個實(shí)例
接下來,我們先看一個極簡的例子:所有的get請求被調(diào)用前在控制臺輸出一句"get請求的advice觸發(fā)了"。
具體實(shí)現(xiàn)如下:
創(chuàng)建一個AOP切面類,只要在類上加個? @Aspect?注解即可。@Aspect?注解用來描述一個切面類,定義切面類的時候需要打上這個注解。@Component?注解將該類交給 Spring 來管理。在這個類里實(shí)現(xiàn)advice:
package?com.mu.demo.advice;
import?org.aspectj.lang.annotation.Aspect;
import?org.aspectj.lang.annotation.Before;
import?org.aspectj.lang.annotation.Pointcut;
import?org.springframework.stereotype.Component;
@Aspect
@Component
public?class?LogAdvice?{
????//?定義一個切點(diǎn):所有被GetMapping注解修飾的方法會織入advice
????@Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)")
????private?void?logAdvicePointcut()?{}
?//?Before表示logAdvice將在目標(biāo)方法執(zhí)行前執(zhí)行
????@Before("logAdvicePointcut()")
????public?void?logAdvice(){
?????//?這里只是一個示例,你可以寫任何處理邏輯
????????System.out.println("get請求的advice觸發(fā)了");
????}
}
創(chuàng)建一個接口類,內(nèi)部創(chuàng)建一個get請求:
package?com.mu.demo.controller;
import?com.alibaba.fastjson.JSON;
import?com.alibaba.fastjson.JSONObject;
import?org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping(value?=?"/aop")
public?class?AopController?{
????@GetMapping(value?=?"/getTest")
????public?JSONObject?aopTest()?{
????????return?JSON.parseObject("{\"message\":\"SUCCESS\",\"code\":200}");
????}
????
?@PostMapping(value?=?"/postTest")
????public?JSONObject?aopTest2(@RequestParam("id")?String?id)?{
????????return?JSON.parseObject("{\"message\":\"SUCCESS\",\"code\":200}");
????}
}
項(xiàng)目啟動后,請求http://localhost:8085/aop/getTest接口:

請求http://localhost:8085/aop/postTest接口,控制臺無輸出,證明切點(diǎn)確實(shí)是只針對被GetMapping修飾的方法。
2.2 第二個實(shí)例
下面我們將問題復(fù)雜化一些,該例的場景是:
自定義一個注解
PermissionsAnnotation創(chuàng)建一個切面類,切點(diǎn)設(shè)置為攔截所有標(biāo)注
PermissionsAnnotation的方法,截取到接口的參數(shù),進(jìn)行簡單的權(quán)限校驗(yàn)將
PermissionsAnnotation標(biāo)注在測試接口類的測試接口test上
具體的實(shí)現(xiàn)步驟:
使用 @Target、@Retention、@Documented自定義一個注解:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public?@interface?PermissionAnnotation{
}
創(chuàng)建第一個AOP切面類,,只要在類上加個? @Aspect?注解即可。@Aspect?注解用來描述一個切面類,定義切面類的時候需要打上這個注解。@Component?注解將該類交給 Spring 來管理。在這個類里實(shí)現(xiàn)第一步權(quán)限校驗(yàn)邏輯:
package?com.example.demo;
import?com.alibaba.fastjson.JSON;
import?com.alibaba.fastjson.JSONObject;
import?org.aspectj.lang.ProceedingJoinPoint;
import?org.aspectj.lang.annotation.Around;
import?org.aspectj.lang.annotation.Aspect;
import?org.aspectj.lang.annotation.Pointcut;
import?org.springframework.core.annotation.Order;
import?org.springframework.stereotype.Component;
@Aspect
@Component
@Order(1)
public?class?PermissionFirstAdvice?{
?//?定義一個切面,括號內(nèi)寫入第1步中自定義注解的路徑
????@Pointcut("@annotation(com.mu.demo.annotation.PermissionAnnotation)")
????private?void?permissionCheck()?{
????}
????@Around("permissionCheck()")
????public?Object?permissionCheckFirst(ProceedingJoinPoint?joinPoint)?throws?Throwable?{
????????System.out.println("===================第一個切面===================:"?+?System.currentTimeMillis());
????????//獲取請求參數(shù),詳見接口類
????????Object[]?objects?=?joinPoint.getArgs();
????????Long?id?=?((JSONObject)?objects[0]).getLong("id");
????????String?name?=?((JSONObject)?objects[0]).getString("name");
????????System.out.println("id1->>>>>>>>>>>>>>>>>>>>>>"?+?id);
????????System.out.println("name1->>>>>>>>>>>>>>>>>>>>>>"?+?name);
????????//?id小于0則拋出非法id的異常
????????if?(id?0)?{
????????????return?JSON.parseObject("{\"message\":\"illegal?id\",\"code\":403}");
????????}
????????return?joinPoint.proceed();
????}
}
創(chuàng)建接口類,并在目標(biāo)方法上標(biāo)注自定義注解? PermissionsAnnotation:
package?com.example.demo;
import?com.alibaba.fastjson.JSON;
import?com.alibaba.fastjson.JSONObject;
import?org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping(value?=?"/permission")
public?class?TestController?{
????@RequestMapping(value?=?"/check",?method?=?RequestMethod.POST)
????//?添加這個注解
????@PermissionsAnnotation()
????public?JSONObject?getGroupList(@RequestBody?JSONObject?request)?{
????????return?JSON.parseObject("{\"message\":\"SUCCESS\",\"code\":200}");
????}
}
在這里,我們先進(jìn)行一個測試。首先,填好請求地址和header:

其次,構(gòu)造正常的參數(shù):

可以拿到正常的響應(yīng)結(jié)果:

然后,構(gòu)造一個異常參數(shù),再次請求:

響應(yīng)結(jié)果顯示,切面類進(jìn)行了判斷,并返回相應(yīng)結(jié)果:

有人會問,如果我一個接口想設(shè)置多個切面類進(jìn)行校驗(yàn)怎么辦?這些切面的執(zhí)行順序如何管理?
很簡單,一個自定義的AOP注解可以對應(yīng)多個切面類,這些切面類執(zhí)行順序由@Order注解管理,該注解后的數(shù)字越小,所在切面類越先執(zhí)行。
下面在實(shí)例中進(jìn)行演示:
創(chuàng)建第二個AOP切面類,在這個類里實(shí)現(xiàn)第二步權(quán)限校驗(yàn):
package?com.example.demo;
import?com.alibaba.fastjson.JSON;
import?com.alibaba.fastjson.JSONObject;
import?org.aspectj.lang.ProceedingJoinPoint;
import?org.aspectj.lang.annotation.Around;
import?org.aspectj.lang.annotation.Aspect;
import?org.aspectj.lang.annotation.Pointcut;
import?org.springframework.core.annotation.Order;
import?org.springframework.stereotype.Component;
@Aspect
@Component
@Order(0)
public?class?PermissionSecondAdvice?{
???@Pointcut("@annotation(com.example.demo.PermissionsAnnotation)")
???private?void?permissionCheck()?{
???}
???@Around("permissionCheck()")
???public?Object?permissionCheckSecond(ProceedingJoinPoint?joinPoint)?throws?Throwable?{
???????System.out.println("===================第二個切面===================:"?+?System.currentTimeMillis());
???????//獲取請求參數(shù),詳見接口類
???????Object[]?objects?=?joinPoint.getArgs();
???????Long?id?=?((JSONObject)?objects[0]).getLong("id");
???????String?name?=?((JSONObject)?objects[0]).getString("name");
???????System.out.println("id->>>>>>>>>>>>>>>>>>>>>>"?+?id);
???????System.out.println("name->>>>>>>>>>>>>>>>>>>>>>"?+?name);
???????//?name不是管理員則拋出異常
???????if?(!name.equals("admin"))?{
???????????return?JSON.parseObject("{\"message\":\"not?admin\",\"code\":403}");
???????}
???????return?joinPoint.proceed();
???}
}
重啟項(xiàng)目,繼續(xù)測試,構(gòu)造兩個參數(shù)都異常的情況:

響應(yīng)結(jié)果,表面第二個切面類執(zhí)行順序更靠前:

3 AOP相關(guān)注解
上面的案例中,用到了諸多注解,下面針對這些注解進(jìn)行詳解。
3.1 @Pointcut
@Pointcut?注解,用來定義一個切面,即上文中所關(guān)注的某件事情的入口,切入點(diǎn)定義了事件觸發(fā)時機(jī)。
@Aspect
@Component
public?class?LogAspectHandler?{
????/**
?????*?定義一個切面,攔截?com.itcodai.course09.controller?包和子包下的所有方法
?????*/
????@Pointcut("execution(*?com.mutest.controller..*.*(..))")
????public?void?pointCut()?{}
}
@Pointcut 注解指定一個切面,定義需要攔截的東西,這里介紹兩個常用的表達(dá)式:一個是使用?execution(),另一個是使用?annotation()。
更多參考:SpringBoot內(nèi)容聚合
execution表達(dá)式:
以?execution(* * com.mutest.controller..*.*(..)))?表達(dá)式為例:
第一個 * 號的位置:表示返回值類型,* 表示所有類型。
包名:表示需要攔截的包名,后面的兩個句點(diǎn)表示當(dāng)前包和當(dāng)前包的所有子包,在本例中指 com.mutest.controller包、子包下所有類的方法。
第二個 * 號的位置:表示類名,* 表示所有類。
*(..):這個星號表示方法名,* 表示所有的方法,后面括弧里面表示方法的參數(shù),兩個句點(diǎn)表示任何參數(shù)。
annotation() 表達(dá)式:
annotation()?方式是針對某個注解來定義切面,比如我們對具有 @PostMapping 注解的方法做切面,可以如下定義切面:
@Pointcut("@annotation(org.springframework.web.bind.annotation.PostMapping)")
public?void?annotationPointcut()?{}
然后使用該切面的話,就會切入注解是?@PostMapping?的所有方法。這種方式很適合處理?@GetMapping、@PostMapping、@DeleteMapping不同注解有各種特定處理邏輯的場景。
還有就是如上面案例所示,針對自定義注解來定義切面。
@Pointcut("@annotation(com.example.demo.PermissionsAnnotation)")
private?void?permissionCheck()?{}
3.2 @Around
@Around注解用于修飾Around增強(qiáng)處理,Around增強(qiáng)處理非常強(qiáng)大,表現(xiàn)在:
@Around可以自由選擇增強(qiáng)動作與目標(biāo)方法的執(zhí)行順序,也就是說可以在增強(qiáng)動作前后,甚至過程中執(zhí)行目標(biāo)方法。這個特性的實(shí)現(xiàn)在于,調(diào)用ProceedingJoinPoint參數(shù)的procedd()方法才會執(zhí)行目標(biāo)方法。@Around可以改變執(zhí)行目標(biāo)方法的參數(shù)值,也可以改變執(zhí)行目標(biāo)方法之后的返回值。
Around增強(qiáng)處理有以下特點(diǎn):
當(dāng)定義一個
Around增強(qiáng)處理方法時,該方法的第一個形參必須是?ProceedingJoinPoint?類型(至少一個形參)。在增強(qiáng)處理方法體內(nèi),調(diào)用ProceedingJoinPoint的proceed方法才會執(zhí)行目標(biāo)方法:這就是@Around增強(qiáng)處理可以完全控制目標(biāo)方法執(zhí)行時機(jī)、如何執(zhí)行的關(guān)鍵;如果程序沒有調(diào)用ProceedingJoinPoint的proceed方法,則目標(biāo)方法不會執(zhí)行。調(diào)用
ProceedingJoinPoint的proceed方法時,還可以傳入一個Object[ ]對象,該數(shù)組中的值將被傳入目標(biāo)方法作為實(shí)參——這就是Around增強(qiáng)處理方法可以改變目標(biāo)方法參數(shù)值的關(guān)鍵。這就是如果傳入的Object[ ]數(shù)組長度與目標(biāo)方法所需要的參數(shù)個數(shù)不相等,或者Object[ ]數(shù)組元素與目標(biāo)方法所需參數(shù)的類型不匹配,程序就會出現(xiàn)異常。
@Around功能雖然強(qiáng)大,但通常需要在線程安全的環(huán)境下使用。因此,如果使用普通的Before、AfterReturning就能解決的問題,就沒有必要使用Around了。如果需要目標(biāo)方法執(zhí)行之前和之后共享某種狀態(tài)數(shù)據(jù),則應(yīng)該考慮使用Around。尤其是需要使用增強(qiáng)處理阻止目標(biāo)的執(zhí)行,或需要改變目標(biāo)方法的返回值時,則只能使用Around增強(qiáng)處理了。
下面,在前面例子上做一些改造,來觀察@Around的特點(diǎn)。
自定義注解類不變。首先,定義接口類:
package?com.example.demo;
import?com.alibaba.fastjson.JSON;
import?com.alibaba.fastjson.JSONObject;
import?org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping(value?=?"/permission")
public?class?TestController?{
????@RequestMapping(value?=?"/check",?method?=?RequestMethod.POST)
????@PermissionsAnnotation()
????public?JSONObject?getGroupList(@RequestBody?JSONObject?request)?{
????????return?JSON.parseObject("{\"message\":\"SUCCESS\",\"code\":200,\"data\":"?+?request?+?"}");
????}
}
唯一切面類(前面案例有兩個切面類,這里只需保留一個即可):
package?com.example.demo;
import?com.alibaba.fastjson.JSONObject;
import?org.aspectj.lang.ProceedingJoinPoint;
import?org.aspectj.lang.annotation.Around;
import?org.aspectj.lang.annotation.Aspect;
import?org.aspectj.lang.annotation.Pointcut;
import?org.springframework.core.annotation.Order;
import?org.springframework.stereotype.Component;
@Aspect
@Component
@Order(1)
public?class?PermissionAdvice?{
????@Pointcut("@annotation(com.example.demo.PermissionsAnnotation)")
????private?void?permissionCheck()?{
????}
????@Around("permissionCheck()")
????public?Object?permissionCheck(ProceedingJoinPoint?joinPoint)?throws?Throwable?{
????????System.out.println("===================開始增強(qiáng)處理===================");
????????//獲取請求參數(shù),詳見接口類
????????Object[]?objects?=?joinPoint.getArgs();
????????Long?id?=?((JSONObject)?objects[0]).getLong("id");
????????String?name?=?((JSONObject)?objects[0]).getString("name");
????????System.out.println("id1->>>>>>>>>>>>>>>>>>>>>>"?+?id);
????????System.out.println("name1->>>>>>>>>>>>>>>>>>>>>>"?+?name);
??//?修改入?yún)?/span>
????????JSONObject?object?=?new?JSONObject();
????????object.put("id",?8);
????????object.put("name",?"lisi");
????????objects[0]?=?object;
??
??//?將修改后的參數(shù)傳入
????????return?joinPoint.proceed(objects);
????}
}
同樣使用JMeter調(diào)用接口,傳入?yún)?shù):{"id":-5,"name":"admin"},響應(yīng)結(jié)果表明:@Around截取到了接口的入?yún)?,并使接口返回了切面類中的結(jié)果。
3.3 @Before
@Before?注解指定的方法在切面切入目標(biāo)方法之前執(zhí)行,可以做一些?Log?處理,也可以做一些信息的統(tǒng)計(jì),比如獲取用戶的請求?URL?以及用戶的?IP?地址等等,這個在做個人站點(diǎn)的時候都能用得到,都是常用的方法。例如下面代碼:
@Aspect
@Component
@Slf4j
public?class?LogAspectHandler?{
????/**
?????*?在上面定義的切面方法之前執(zhí)行該方法
?????*?@param?joinPoint?jointPoint
?????*/
????@Before("pointCut()")
????public?void?doBefore(JoinPoint?joinPoint)?{
????????log.info("====doBefore方法進(jìn)入了====");
????????//?獲取簽名
????????Signature?signature?=?joinPoint.getSignature();
????????//?獲取切入的包名
????????String?declaringTypeName?=?signature.getDeclaringTypeName();
????????//?獲取即將執(zhí)行的方法名
????????String?funcName?=?signature.getName();
????????log.info("即將執(zhí)行方法為:?{},屬于{}包",?funcName,?declaringTypeName);
????????//?也可以用來記錄一些信息,比如獲取請求的?URL?和?IP
????????ServletRequestAttributes?attributes?=?(ServletRequestAttributes)?RequestContextHolder.getRequestAttributes();
????????HttpServletRequest?request?=?attributes.getRequest();
????????//?獲取請求?URL
????????String?url?=?request.getRequestURL().toString();
????????//?獲取請求?IP
????????String?ip?=?request.getRemoteAddr();
????????log.info("用戶請求的url為:{},ip地址為:{}",?url,?ip);
????}
}
JointPoint?對象很有用,可以用它來獲取一個簽名,利用簽名可以獲取請求的包名、方法名,包括參數(shù)(通過?joinPoint.getArgs()?獲?。┑?。
3.4 @After
@After?注解和?@Before?注解相對應(yīng),指定的方法在切面切入目標(biāo)方法之后執(zhí)行,也可以做一些完成某方法之后的 Log 處理。
@Aspect
@Component
@Slf4j
public?class?LogAspectHandler?{
????/**
?????*?定義一個切面,攔截?com.mutest.controller?包下的所有方法
?????*/
????@Pointcut("execution(*?com.mutest.controller..*.*(..))")
????public?void?pointCut()?{}
????/**
?????*?在上面定義的切面方法之后執(zhí)行該方法
?????*?@param?joinPoint?jointPoint
?????*/
????@After("pointCut()")
????public?void?doAfter(JoinPoint?joinPoint)?{
????????log.info("====?doAfter?方法進(jìn)入了====");
????????Signature?signature?=?joinPoint.getSignature();
????????String?method?=?signature.getName();
????????log.info("方法{}已經(jīng)執(zhí)行完",?method);
????}
}
到這里,我們來寫個 Controller 測試一下執(zhí)行結(jié)果,新建一個 AopController 如下:
@RestController
@RequestMapping("/aop")
public?class?AopController?{
????@GetMapping("/{name}")
????public?String?testAop(@PathVariable?String?name)?{
????????return?"Hello?"?+?name;
????}
}
啟動項(xiàng)目,在瀏覽器中輸入:localhost:8080/aop/csdn,觀察一下控制臺的輸出信息:
====doBefore?方法進(jìn)入了====??
即將執(zhí)行方法為:?testAop,屬于com.itcodai.mutest.AopController包??
用戶請求的 url 為:http://localhost:8080/aop/name,ip地址為:0:0:0:0:0:0:0:1 ?
====?doAfter?方法進(jìn)入了====??
方法?testAop?已經(jīng)執(zhí)行完
從打印出來的?Log?中可以看出程序執(zhí)行的邏輯與順序,可以很直觀的掌握?@Before?和?@After?兩個注解的實(shí)際作用。
3.5 @AfterReturning
@AfterReturning?注解和?@After?有些類似,區(qū)別在于?@AfterReturning?注解可以用來捕獲切入方法執(zhí)行完之后的返回值,對返回值進(jìn)行業(yè)務(wù)邏輯上的增強(qiáng)處理,例如:
@Aspect
@Component
@Slf4j
public?class?LogAspectHandler?{
????/**
?????*?在上面定義的切面方法返回后執(zhí)行該方法,可以捕獲返回對象或者對返回對象進(jìn)行增強(qiáng)
?????*?@param?joinPoint?joinPoint
?????*?@param?result?result
?????*/
????@AfterReturning(pointcut?=?"pointCut()",?returning?=?"result")
????public?void?doAfterReturning(JoinPoint?joinPoint,?Object?result)?{
????????Signature?signature?=?joinPoint.getSignature();
????????String?classMethod?=?signature.getName();
????????log.info("方法{}執(zhí)行完畢,返回參數(shù)為:{}",?classMethod,?result);
????????//?實(shí)際項(xiàng)目中可以根據(jù)業(yè)務(wù)做具體的返回值增強(qiáng)
????????log.info("對返回參數(shù)進(jìn)行業(yè)務(wù)上的增強(qiáng):{}",?result?+?"增強(qiáng)版");
????}
}
需要注意的是,在?@AfterReturning?注解 中,屬性?returning?的值必須要和參數(shù)保持一致,否則會檢測不到。該方法中的第二個入?yún)⒕褪潜磺蟹椒ǖ姆祷刂?,?doAfterReturning?方法中可以對返回值進(jìn)行增強(qiáng),可以根據(jù)業(yè)務(wù)需要做相應(yīng)的封裝。我們重啟一下服務(wù),再測試一下:
方法 testAop 執(zhí)行完畢,返回參數(shù)為:Hello CSDN ?
對返回參數(shù)進(jìn)行業(yè)務(wù)上的增強(qiáng):Hello CSDN 增強(qiáng)版
3.6 @AfterThrowing
當(dāng)被切方法執(zhí)行過程中拋出異常時,會進(jìn)入?@AfterThrowing?注解的方法中執(zhí)行,在該方法中可以做一些異常的處理邏輯。要注意的是?throwing?屬性的值必須要和參數(shù)一致,否則會報(bào)錯。該方法中的第二個入?yún)⒓礊閽伋龅漠惓!?/span>
@Aspect
@Component
@Slf4j
public?class?LogAspectHandler?{
????/**
?????*?在上面定義的切面方法執(zhí)行拋異常時,執(zhí)行該方法
?????*?@param?joinPoint?jointPoint
?????*?@param?ex?ex
?????*/
????@AfterThrowing(pointcut?=?"pointCut()",?throwing?=?"ex")
????public?void?afterThrowing(JoinPoint?joinPoint,?Throwable?ex)?{
????????Signature?signature?=?joinPoint.getSignature();
????????String?method?=?signature.getName();
????????//?處理異常的邏輯
????????log.info("執(zhí)行方法{}出錯,異常為:{}",?method,?ex);
????}
}
以上就是AOP的全部內(nèi)容。通過幾個例子就可以感受到,AOP的便捷之處。
碼字不易,如果你覺得讀完以后有收獲,不妨點(diǎn)個在看讓更多的人看到吧~
往期推薦

下方二維碼關(guān)注我

技術(shù)草根,堅(jiān)持分享?編程,算法,架構(gòu)
