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 優(yōu)雅的實(shí)現(xiàn)權(quán)限校驗(yàn)?。╒IP典藏版)

        共 24420字,需瀏覽 49分鐘

         ·

        2023-10-06 21:33

        關(guān)注我們,設(shè)為星標(biāo),每天7:40不見不散,架構(gòu)路上與您共享

        回復(fù)架構(gòu)師獲取資源


        大家好,我是你們的朋友架構(gòu)君,一個(gè)會(huì)寫代碼吟詩(shī)的架構(gòu)師。

        1 理解AOP

        1.1 什么是AOP

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

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

        有多少業(yè)務(wù)操作,就要寫多少重復(fù)的校驗(yàn)和日志記錄代碼,這顯然是無法接受的。當(dāng)然,用面向?qū)ο蟮乃枷?,我們可以把這些重復(fù)的代碼抽離出來,寫成公共方法,就是下面這樣:

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

        1.2 AOP體系與概念

        簡(jiǎn)單地去理解,其實(shí)AOP要做三類事:

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

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

        一些概念詳解:

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

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

        2 AOP實(shí)例

        實(shí)踐出真知,接下來我們就擼代碼來實(shí)現(xiàn)一下AOP。完整項(xiàng)目已上傳至:GitHub AOP demo項(xiàng)目,該項(xiàng)目是關(guān)于springboot的集成項(xiàng)目,AOP部分請(qǐng)關(guān)注【aop-demo】模塊。

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

        2.1 第一個(gè)實(shí)例

        接下來,我們先看一個(gè)極簡(jiǎn)的例子:所有的get請(qǐng)求被調(diào)用前在控制臺(tái)輸出一句"get請(qǐng)求的advice觸發(fā)了"。

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

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

         // Before表示logAdvice將在目標(biāo)方法執(zhí)行前執(zhí)行
            @Before("logAdvicePointcut()")
            public void logAdvice(){
             // 這里只是一個(gè)示例,你可以寫任何處理邏輯
                System.out.println("get請(qǐng)求的advice觸發(fā)了");
            }
        }

        2.創(chuàng)建一個(gè)接口類,內(nèi)部創(chuàng)建一個(gè)get請(qǐng)求:

        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)目啟動(dòng)后,請(qǐng)求http://localhost:8085/aop/getTest接口:

        請(qǐng)求http://localhost:8085/aop/postTest接口,控制臺(tái)無輸出,證明切點(diǎn)確實(shí)是只針對(duì)被GetMapping修飾的方法。

        2.2 第二個(gè)實(shí)例

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

        • 自定義一個(gè)注解PermissionsAnnotation
        • 創(chuàng)建一個(gè)切面類,切點(diǎn)設(shè)置為攔截所有標(biāo)注PermissionsAnnotation的方法,截取到接口的參數(shù),進(jìn)行簡(jiǎn)單的權(quán)限校驗(yàn)
        • 將PermissionsAnnotation標(biāo)注在測(cè)試接口類的測(cè)試接口test上

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

        1.使用@Target、@Retention、@Documented自定義一個(gè)注解(關(guān)于這三個(gè)注解詳情請(qǐng)見:元注解詳解):

        @Target(ElementType.METHOD)
        @Retention(RetentionPolicy.RUNTIME)
        @Documented
        public @interface PermissionAnnotation{
        }

        2.創(chuàng)建第一個(gè)AOP切面類,,只要在類上加個(gè) @Aspect 注解即可。@Aspect 注解用來描述一個(gè)切面類,定義切面類的時(shí)候需要打上這個(gè)注解。@Component 注解將該類交給 Spring 來管理。在這個(gè)類里實(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 {

         // 定義一個(gè)切面,括號(hào)內(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("===================第一個(gè)切面===================:" + System.currentTimeMillis());

                //獲取請(qǐ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);

                // id小于0則拋出非法id的異常
                if (id < 0) {
                    return JSON.parseObject("{\"message\":\"illegal id\",\"code\":403}");
                }
                return joinPoint.proceed();
            }
        }

        3.創(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)
            // 添加這個(gè)注解
            @PermissionsAnnotation()
            public JSONObject getGroupList(@RequestBody JSONObject request) {
                return JSON.parseObject("{\"message\":\"SUCCESS\",\"code\":200}");
            }
        }

        在這里,我們先進(jìn)行一個(gè)測(cè)試。首先,填好請(qǐng)求地址和header:

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

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

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

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

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

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

        下面在實(shí)例中進(jìn)行演示:

        創(chuàng)建第二個(gè)AOP切面類,在這個(gè)類里實(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.mu.demo.annotation.PermissionAnnotation)")
           private void permissionCheck() {
           }

           @Around("permissionCheck()")
           public Object permissionCheckSecond(ProceedingJoinPoint joinPoint) throws Throwable {
               System.out.println("===================第二個(gè)切面===================:" + System.currentTimeMillis());

               //獲取請(qǐng)求參數(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ù)測(cè)試,構(gòu)造兩個(gè)參數(shù)都異常的情況:

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

        3 AOP相關(guān)注解

        上面的案例中,用到了諸多注解,下面針對(duì)這些注解進(jìn)行詳解。

        3.1 @Pointcut

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

        @Aspect
        @Component
        public class LogAspectHandler {

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

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

        @Pointcut 注解指定一個(gè)切點(diǎn),定義需要攔截的東西,這里介紹兩個(gè)常用的表達(dá)式:一個(gè)是使用 execution(),另一個(gè)是使用 annotation()。

        execution表達(dá)式:

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

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

        annotation() 表達(dá)式:

        annotation() 方式是針對(duì)某個(gè)注解來定義切點(diǎn),比如我們對(duì)具有 @PostMapping 注解的方法做切面,可以如下定義切面:

        @Pointcut("@annotation(org.springframework.web.bind.annotation.PostMapping)")
        public void annotationPointcut() {}

        然后使用該切面的話,就會(huì)切入注解是 @PostMapping 的所有方法。這種方式很適合處理 @GetMapping、@PostMapping、@DeleteMapping不同注解有各種特定處理邏輯的場(chǎng)景。

        還有就是如上面案例所示,針對(duì)自定義注解來定義切面。

        @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)動(dòng)作與目標(biāo)方法的執(zhí)行順序,也就是說可以在增強(qiáng)動(dòng)作前后,甚至過程中執(zhí)行目標(biāo)方法。這個(gè)特性的實(shí)現(xiàn)在于,調(diào)用ProceedingJoinPoint參數(shù)的procedd()方法才會(huì)執(zhí)行目標(biāo)方法。

        • @Around可以改變執(zhí)行目標(biāo)方法的參數(shù)值,也可以改變執(zhí)行目標(biāo)方法之后的返回值。

        Around增強(qiáng)處理有以下特點(diǎn):

        • 當(dāng)定義一個(gè)Around增強(qiáng)處理方法時(shí),該方法的第一個(gè)形參必須是 ProceedingJoinPoint 類型(至少一個(gè)形參)。在增強(qiáng)處理方法體內(nèi),調(diào)用ProceedingJoinPoint的proceed方法才會(huì)執(zhí)行目標(biāo)方法:這就是@Around增強(qiáng)處理可以完全控制目標(biāo)方法執(zhí)行時(shí)機(jī)、如何執(zhí)行的關(guān)鍵;如果程序沒有調(diào)用ProceedingJoinPoint的proceed方法,則目標(biāo)方法不會(huì)執(zhí)行。

        • 調(diào)用ProceedingJoinPoint的proceed方法時(shí),還可以傳入一個(gè)Object[ ]對(duì)象,該數(shù)組中的值將被傳入目標(biāo)方法作為實(shí)參——這就是Around增強(qiáng)處理方法可以改變目標(biāo)方法參數(shù)值的關(guān)鍵。這就是如果傳入的Object[ ]數(shù)組長(zhǎng)度與目標(biāo)方法所需要的參數(shù)個(gè)數(shù)不相等,或者Object[ ]數(shù)組元素與目標(biāo)方法所需參數(shù)的類型不匹配,程序就會(huì)出現(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)方法的返回值時(shí),則只能使用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 + "}");
            }
        }

        唯一切面類(前面案例有兩個(gè)切面類,這里只需保留一個(gè)即可):

        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)處理===================");

                //獲取請(qǐ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ì),比如獲取用戶的請(qǐng)求 URL 以及用戶的 IP 地址等等,這個(gè)在做個(gè)人站點(diǎn)的時(shí)候都能用得到,都是常用的方法。例如下面代碼:

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

            @Pointcut("execution(* com.mutest.controller..*.*(..))")
            public void pointCut() {}
            
            @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);

                // 也可以用來記錄一些信息,比如獲取請(qǐng)求的 URL 和 IP
                ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
                HttpServletRequest request = attributes.getRequest();
                // 獲取請(qǐng)求 URL
                String url = request.getRequestURL().toString();
                // 獲取請(qǐng)求 IP
                String ip = request.getRemoteAddr();
                log.info("用戶請(qǐng)求的url為:{},ip地址為:{}", url, ip);
            }
        }

        JointPoint 對(duì)象很有用,可以用它來獲取一個(gè)簽名,利用簽名可以獲取請(qǐng)求的包名、方法名,包括參數(shù)(通過 joinPoint.getArgs() 獲?。┑?。

        3.4 @After

        @After 注解和 @Before 注解相對(duì)應(yīng),指定的方法在切面切入目標(biāo)方法之后執(zhí)行,也可以做一些完成某方法之后的 Log 處理。

        @Aspect
        @Component
        @Slf4j
        public class LogAspectHandler {
            /**
             * 定義一個(gè)切面,攔截 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);
            }
        }

        到這里,我們來寫個(gè) Controller 測(cè)試一下執(zhí)行結(jié)果,新建一個(gè) AopController 如下:

        @RestController
        @RequestMapping("/aop")
        public class AopController {

            @GetMapping("/{name}")
            public String testAop(@PathVariable String name) {
                return "Hello " + name;
            }
        }

        啟動(dòng)項(xiàng)目,在瀏覽器中輸入:localhost:8080/aop/csdn,觀察一下控制臺(tái)的輸出信息:

        ====doBefore 方法進(jìn)入了====  
        即將執(zhí)行方法為: testAop,屬于com.itcodai.mutest.AopController包  
        用戶請(qǐng)求的 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 兩個(gè)注解的實(shí)際作用。

        3.5 @AfterReturning

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

        @Aspect
        @Component
        @Slf4j
        public class LogAspectHandler {
            /**
             * 在上面定義的切面方法返回后執(zhí)行該方法,可以捕獲返回對(duì)象或者對(duì)返回對(duì)象進(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("對(duì)返回參數(shù)進(jìn)行業(yè)務(wù)上的增強(qiáng):{}", result + "增強(qiáng)版");
            }
        }

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

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

        3.6 @AfterThrowing

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

        @Aspect
        @Component
        @Slf4j
        public class LogAspectHandler {
            /**
             * 在上面定義的切面方法執(zhí)行拋異常時(shí),執(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í)行方法{}出錯(cuò),異常為:{}", method, ex);
            }
        }

        完整的項(xiàng)目已上傳至Gtihub:

        ?
        • https://github.com/ThinkMugz/aopDemo
        ?

        以上就是AOP的全部?jī)?nèi)容。通過幾個(gè)例子就可以感受到,AOP的便捷之處。歡迎大家指出文中不足之處,喜歡的朋友來個(gè)一鍵三連,聽說這么做的人offer拿到手軟( ?? ω ?? )y

           
           
        來源:blog.csdn.net/mu_wind/
        article/details/102758005


        到此文章就結(jié)束了。Java架構(gòu)師必看一個(gè)集公眾號(hào)、小程序、網(wǎng)站(3合1的文章平臺(tái),給您架構(gòu)路上一臂之力)。如果今天的文章對(duì)你在進(jìn)階架構(gòu)師的路上有新的啟發(fā)和進(jìn)步,歡迎轉(zhuǎn)發(fā)給更多人。歡迎加入架構(gòu)師社區(qū)技術(shù)交流群,眾多大咖帶你進(jìn)階架構(gòu)師,在后臺(tái)回復(fù)“加群”即可入群。



        這些年小編給你分享過的干貨


        1.idea永久激活碼(親測(cè)可用)

        2.優(yōu)質(zhì)ERP系統(tǒng)帶進(jìn)銷存財(cái)務(wù)生產(chǎn)功能(附源碼)

        3.優(yōu)質(zhì)SpringBoot帶工作流管理項(xiàng)目(附源碼)

        4.最好用的OA系統(tǒng),拿來即用(附源碼)

        5.SBoot+Vue外賣系統(tǒng)前后端都有(附源碼

        6.SBoot+Vue可視化大屏拖拽項(xiàng)目(附源碼)


        轉(zhuǎn)發(fā)在看就是最大的支持??

        瀏覽 446
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        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>
            毛片免费基地 | 国产一级毛片在线 | 高清无码色 | 岛国激情片 | 91刺激视频 | 韩国青草最新无码自慰GIF | 成人免费视频 在线观看 | 大香蕉网址 | 成人无码ss52ss亚洲AV | 开心激情站中文字幕 |