1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        手把手教你利用切面AOP實現(xiàn)權(quán)限校驗!

        共 4297字,需瀏覽 9分鐘

         ·

        2022-01-26 14:29

        你知道的越多,不知道的就越多,業(yè)余的像一棵小草!

        你來,我們一起精進!你不來,我和你的競爭對手一起精進!

        編輯:業(yè)余草

        blog.csdn.net/mu_wind

        推薦:https://www.xttblog.com/?p=5306

        1 理解AOP

        1.1 什么是AOP

        「AOP(Aspect Oriented Programming)」,面向切面思想,是Spring的三大核心思想之一(兩外兩個:IOC-控制反轉(zhuǎn)、DI-依賴注入)。

        那么AOP為何那么重要呢?在我們的程序中,經(jīng)常存在一些系統(tǒng)性的需求,比如權(quán)限校驗、日志記錄、統(tǒng)計等,這些代碼會散落穿插在各個業(yè)務(wù)邏輯中,非常冗余且不利于維護。例如下面這個示意圖:

        bca465347c74e1ff209dba327258a016.webpAOP權(quán)限控制

        有多少業(yè)務(wù)操作,就要寫多少重復(fù)的校驗和日志記錄代碼,這顯然是無法接受的。當然,用面向?qū)ο蟮乃枷耄覀兛梢园堰@些重復(fù)的代碼抽離出來,寫成公共方法,就是下面這樣:

        4a2cf23e71a9ec722c26255a721efdf9.webp重復(fù)的代碼抽離

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

        66059a61f4a48d23b43bbc2194af33f9.webpAOP權(quán)限校驗

        1.2 AOP體系與概念

        簡單地去理解,其實AOP要做三類事:

        • 在哪里切入,也就是權(quán)限校驗等非業(yè)務(wù)操作在哪些業(yè)務(wù)代碼中執(zhí)行。
        • 在什么時候切入,是業(yè)務(wù)代碼執(zhí)行前還是執(zhí)行后。
        • 切入后做什么事,比如做權(quán)限校驗、日志記錄等。

        因此,AOP的體系可以梳理為下圖:

        8bf6727d1db1b77365778b7bb0498a79.webpAOP的體系

        一些概念詳解:

        • Pointcut:切點,決定處理如權(quán)限校驗、日志記錄等在何處切入業(yè)務(wù)代碼中(即織入切面)。切點分為execution方式和annotation方式。前者可以用路徑表達式指定哪些類織入切面,后者可以指定被哪些注解修飾的代碼織入切面。
        • Advice:處理,包括處理時機和處理內(nèi)容。處理內(nèi)容就是要做什么事,比如校驗權(quán)限和記錄日志。處理時機就是在什么時機執(zhí)行處理內(nèi)容,分為前置處理(即業(yè)務(wù)代碼執(zhí)行前)、后置處理(業(yè)務(wù)代碼執(zhí)行后)等。
        • Aspect:切面,即PointcutAdvice。
        • Joint point:連接點,是程序執(zhí)行的一個點。例如,一個方法的執(zhí)行或者一個異常的處理。在 Spring AOP 中,一個連接點總是代表一個方法執(zhí)行。
        • Weaving:織入,就是通過動態(tài)代理,在目標對象方法中執(zhí)行處理內(nèi)容的過程。

        網(wǎng)絡(luò)上有張圖,我覺得非常傳神,貼在這里供大家觀詳:

        714d59579bcaae019988b7d1c5f824de.webpAOP的體系

        2 AOP實例

        實踐出真知,接下來我們就擼代碼來實現(xiàn)一下AOP。完整項目加我微信:xttblog2,領(lǐng)取。該項目是關(guān)于springboot的集成項目,AOP部分請關(guān)注【aop-demo】模塊。

        使用 AOP,首先需要引入 「AOP 的依賴」。


        ????<groupId>org.springframework.bootgroupId>
        ????<artifactId>spring-boot-starter-aopartifactId>
        </dependency>

        2.1 第一個實例

        接下來,我們先看一個極簡的例子:「所有的get請求被調(diào)用前在控制臺輸出一句"get請求的advice觸發(fā)了"?!?/strong>

        具體實現(xiàn)如下:

        1. 「創(chuàng)建一個AOP切面類,只要在類上加個 @Aspect 注解即可。@Aspect 注解用來描述一個切面類,定義切面類的時候需要打上這個注解。@Component 注解將該類交給 Spring 來管理。在這個類里實現(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?{
        ????//?定義一個切點:所有被GetMapping注解修飾的方法會織入advice
        ????@Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)")
        ????private?void?logAdvicePointcut()?{}

        ?//?Before表示logAdvice將在目標方法執(zhí)行前執(zhí)行
        ????@Before("logAdvicePointcut()")
        ????public?void?logAdvice(){
        ?????//?這里只是一個示例,你可以寫任何處理邏輯
        ????????System.out.println("get請求的advice觸發(fā)了");
        ????}
        }
        1. 創(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}");
        ????}
        }

        項目啟動后,請求http://localhost:8085/aop/getTest接口:

        26964973795a1f32e10d4dab13c763cf.webpAOP項目啟動

        請求http://localhost:8085/aop/postTest接口,控制臺無輸出,證明切點確實是只針對被GetMapping修飾的方法。

        2.2 第二個實例

        下面我們將問題復(fù)雜化一些,該例的場景是:

        1. 自定義一個注解PermissionsAnnotation
        2. 創(chuàng)建一個切面類,切點設(shè)置為攔截所有標注PermissionsAnnotation的方法,「截取到接口的參數(shù)」,進行簡單的權(quán)限校驗
        3. PermissionsAnnotation標注在測試接口類的測試接口test

        具體的實現(xiàn)步驟:

        1. 「使用@Target、@Retention、@Documented自定義一個注解:」
        @Target(ElementType.METHOD)
        @Retention(RetentionPolicy.RUNTIME)
        @Documented
        public?@interface?PermissionAnnotation{
        }
        1. 「創(chuàng)建第一個AOP切面類,,只要在類上加個 @Aspect 注解即可。@Aspect 注解用來描述一個切面類,定義切面類的時候需要打上這個注解。@Component 注解將該類交給 Spring 來管理。在這個類里實現(xiàn)第一步權(quá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();
        ????}
        }
        1. 「創(chuàng)建接口類,并在目標方法上標注自定義注解 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}");
        ????}
        }

        在這里,我們先進行一個測試。首先,填好請求地址和header:

        fdedcd287f46d9a20cc86fcdd899029d.webp填好請求地址和header

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

        69534f3bd93807a3dc39371542c9e7ca.webp構(gòu)造正常的參數(shù)

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

        909dd76482aadff6067d495ef2225232.webp正常的響應(yīng)結(jié)果

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

        e2460b63097cad1fbd42d472d10bc8a4.webp再次請求

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

        65ecffed03039f349e15f9f60fac89bd.webp返回相應(yīng)結(jié)果

        「有人會問,如果我一個接口想設(shè)置多個切面類進行校驗怎么辦?這些切面的執(zhí)行順序如何管理」

        很簡單,一個自定義的AOP注解可以對應(yīng)多個切面類,這些切面類執(zhí)行順序由@Order注解管理,該注解后的數(shù)字越小,所在切面類越先執(zhí)行。

        「下面在實例中進行演示:」

        創(chuàng)建第二個AOP切面類,在這個類里實現(xiàn)第二步權(quá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.mu.demo.annotation.PermissionAnnotation)")
        ???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();
        ???}
        }

        重啟項目,繼續(xù)測試,構(gòu)造兩個參數(shù)都異常的情況:

        3ad4f486fb74b2efd1d704b48dccf9e2.webp重啟項目

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

        317676c68461af5c5e97484b0f39ccbf.webp切面類執(zhí)行順序

        3 AOP相關(guān)注解

        上面的案例中,用到了諸多注解,下面針對這些注解進行詳解。

        3.1 @Pointcut

        @Pointcut 注解,用來定義一個切點,即上文中所關(guān)注的某件事情的入口,切入點定義了事件觸發(fā)時機。

        @Aspect
        @Component
        public?class?LogAspectHandler?{

        ????/**
        ?????*?定義一個切面,攔截?com.mutest.controller?包和子包下的所有方法
        ?????*/

        ????@Pointcut("execution(*?com.mutest.controller..*.*(..))")
        ????public?void?pointCut()?{}
        }

        @Pointcut 注解指定一個切點,定義需要攔截的東西,這里介紹兩個常用的表達式:「一個是使用 execution(),另一個是使用 annotation()?!?/strong>

        「execution表達式:」

        execution(* com.mutest.controller..*.*(..))) 表達式為例:

        • 第一個 * 號的位置:表示返回值類型,* 表示所有類型。
        • 包名:表示需要攔截的包名,后面的兩個句點表示當前包和當前包的所有子包,在本例中指 com.mutest.controller包、子包下所有類的方法。
        • 第二個 * 號的位置:表示類名,* 表示所有類。
        • *(..):這個星號表示方法名,* 表示所有的方法,后面括弧里面表示方法的參數(shù),兩個句點表示任何參數(shù)。

        「annotation() 表達式:」

        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增強處理,Around增強處理非常強大,表現(xiàn)在:

        1. @Around可以自由選擇增強動作與目標方法的執(zhí)行順序,也就是說可以在增強動作前后,甚至過程中執(zhí)行目標方法。這個特性的實現(xiàn)在于,調(diào)用ProceedingJoinPoint參數(shù)的procedd()方法才會執(zhí)行目標方法。
        2. @Around可以改變執(zhí)行目標方法的參數(shù)值,也可以改變執(zhí)行目標方法之后的返回值。

        Around增強處理有以下特點:

        1. 當定義一個Around增強處理方法時,該方法的「第一個形參必須是 ProceedingJoinPoint 類型」(至少一個形參)。在增強處理方法體內(nèi),調(diào)用ProceedingJoinPointproceed方法才會執(zhí)行目標方法:這就是@Around增強處理可以完全控制目標方法執(zhí)行時機、如何執(zhí)行的關(guān)鍵;如果程序沒有調(diào)用ProceedingJoinPointproceed方法,則目標方法不會執(zhí)行。
        2. 調(diào)用ProceedingJoinPointproceed方法時,還可以傳入一個Object[ ]對象,該數(shù)組中的值將被傳入目標方法作為實參——「這就是Around增強處理方法可以改變目標方法參數(shù)值的關(guān)鍵」。這就是如果傳入的Object[ ]數(shù)組長度與目標方法所需要的參數(shù)個數(shù)不相等,或者Object[ ]數(shù)組元素與目標方法所需參數(shù)的類型不匹配,程序就會出現(xiàn)異常。

        @Around功能雖然強大,但通常需要在線程安全的環(huán)境下使用。因此,如果使用普通的BeforeAfterReturning就能解決的問題,就沒有必要使用Around了。如果需要目標方法執(zhí)行之前和之后共享某種狀態(tài)數(shù)據(jù),則應(yīng)該考慮使用Around。尤其是需要使用增強處理阻止目標的執(zhí)行,或需要改變目標方法的返回值時,則只能使用Around增強處理了。

        下面,在前面例子上做一些改造,來觀察@Around的特點。

        自定義注解類不變。首先,定義接口類:

        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("===================開始增強處理===================");

        ????????//獲取請求參數(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)ⅲ⑹菇涌诜祷亓饲忻骖愔械慕Y(jié)果。

        0d168f597b34e7c837dff017c2e1940a.webpJMeter調(diào)用接口

        3.3 @Before

        @Before 注解指定的方法在切面切入目標方法之前執(zhí)行」,可以做一些 Log 處理,也可以做一些信息的統(tǒng)計,比如獲取用戶的請求 URL 以及用戶的 IP 地址等等,這個在做個人站點的時候都能用得到,都是常用的方法。例如下面代碼:

        @Aspect
        @Component
        @Slf4j
        public?class?LogAspectHandler?{
        ????/**
        ?????*?在上面定義的切面方法之前執(zhí)行該方法
        ?????*?@param?joinPoint?jointPoint
        ?????*/

        ????@Before("pointCut()")
        ????public?void?doBefore(JoinPoint?joinPoint)?{
        ????????log.info("====doBefore方法進入了====");

        ????????//?獲取簽名
        ????????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),指定的方法在切面切入目標方法之后執(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?方法進入了====");
        ????????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;
        ????}
        }

        啟動項目,在瀏覽器中輸入:localhost:8080/aop/csdn,觀察一下控制臺的輸出信息:

        ====doBefore?方法進入了====??
        即將執(zhí)行方法為:?testAop,屬于com.itcodai.mutest.AopController包??
        用戶請求的 url 為:http://localhost:8080/aop/name,ip地址為:0:0:0:0:0:0:0:1 ?
        ====?doAfter?方法進入了====??
        方法?testAop?已經(jīng)執(zhí)行完

        從打印出來的 Log 中可以看出程序執(zhí)行的邏輯與順序,可以很直觀的掌握 @Before@After 兩個注解的實際作用。

        3.5 @AfterReturning

        @AfterReturning 注解和 @After 有些類似,區(qū)別在于 @AfterReturning 注解可以用來捕獲切入方法執(zhí)行完之后的返回值,對返回值進行業(yè)務(wù)邏輯上的增強處理,例如:

        @Aspect
        @Component
        @Slf4j
        public?class?LogAspectHandler?{
        ????/**
        ?????*?在上面定義的切面方法返回后執(zhí)行該方法,可以捕獲返回對象或者對返回對象進行增強
        ?????*?@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);
        ????????//?實際項目中可以根據(jù)業(yè)務(wù)做具體的返回值增強
        ????????log.info("對返回參數(shù)進行業(yè)務(wù)上的增強:{}",?result?+?"增強版");
        ????}
        }

        需要注意的是,在 @AfterReturning 注解 中,屬性 returning 的值必須要和參數(shù)保持一致,否則會檢測不到。該方法中的第二個入?yún)⒕褪潜磺蟹椒ǖ姆祷刂?,?doAfterReturning 方法中可以對返回值進行增強,可以根據(jù)業(yè)務(wù)需要做相應(yīng)的封裝。我們重啟一下服務(wù),再測試一下:

        方法 testAop 執(zhí)行完畢,返回參數(shù)為:Hello CSDN ?
        對返回參數(shù)進行業(yè)務(wù)上的增強:Hello CSDN 增強版

        3.6 @AfterThrowing

        當被切方法執(zhí)行過程中拋出異常時,會進入 @AfterThrowing 注解的方法中執(zhí)行,在該方法中可以做一些異常的處理邏輯。要注意的是 throwing 屬性的值必須要和參數(shù)一致,否則會報錯。該方法中的第二個入?yún)⒓礊閽伋龅漠惓!?/p>

        @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);
        ????}
        }

        完整的項目加我微信:xttblog2,我分享給你。聽說這么做的人2021年都升職加薪了!

        以上就是AOP的全部內(nèi)容。通過幾個例子就可以感受到,AOP 的便捷之處。歡迎大家指出文中不足之處,喜歡的朋友點贊收藏!

        瀏覽 33
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

          <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            影音先锋资源 | 欧美激情一区二区A片成人牛牛 | 激情视频网址 | 成人视频在线观看高清无码网站免费 | 国产一级婬乱A片AAA毛多网站 | 黄色二区 | 搜索黄色电影 | 亚洲免费电影网 | 亚洲成人高清无码在线 | 日韩欧美在线视频播放 |