Spring Boot Aop 執(zhí)行順序

Spring Boot Aop 執(zhí)行順序

1. 概述

在 spring boot 項目中,使用 aop 增強,不僅可以很優(yōu)雅地擴展功能,還可以讓一寫多用,避免寫重復(fù)代碼,例如:記錄接口耗時,記錄接口日志,接口權(quán)限,等等。所以,在項目中學(xué)習(xí)并使用 aop ,是十分必要的。然而,當我們在一個接口中使用多個 aop,時,就需要注意他們的執(zhí)行順序了。那么,它們的執(zhí)行順序是怎樣的呢?如果不把這個問題搞明白,那我們的程序就不可控,這是不允許的,這就是我們今天要討論的問題。

2. 實現(xiàn) AOP

2.1 通過注解實現(xiàn) AOP

MyAop:

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

MyAspect:

@Aspect
@Component
public class MyAspect {

    @Around("@annotation(aop)")
    public Object around(ProceedingJoinPoint joinPoint, 
                         MyAop aop) throws Throwable {
        return joinPoint.proceed();
    }

}

SampleController#myApi:

@RestController
@RequestMapping("/sample")
public class SampleController {

    @MyAop
    @RequestMapping("/my-api")
    public String myApi() {
        return "success";
    }

}

這樣,我們就通過使用注解的方式實現(xiàn)了 AOP 。

2.2 通過掃描包

比如,我們有這樣一個接口 SampleController#myApi2:

@RestController
@RequestMapping("/sample")
public class SampleController {

    @RequestMapping("/my-api2")
    public String myApi2() {
        return "success";
    }

}

我們可以使用包掃描的方式進行攔截:

@Aspect
@Component
public class My2Aspect {

    @Around("execution(* com.fengwenyi.demo.springboot.aop.controller.SampleController.myApi2(..))")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        return joinPoint.proceed();
    }

}

這樣,我們也就通過使用包掃描的方式實現(xiàn)了 AOP 。

3. 多個 AOP

3.1 分析

先提一個疑問:多個AOP注解,執(zhí)行順序是怎么樣的呢?如何設(shè)置執(zhí)行順序呢?

比如,APP 請求我們的 API 接口,在請求到達 API 接口之前,可以先執(zhí)行 AOP1,在執(zhí)行 AOP2,并且順序不能變,如下圖:

Xnip2024-01-16_10-52-00.jpg

我們再拆解一下實際內(nèi)部執(zhí)行邏輯。

請求:請求先進入到 AOP1,再進入到 AOP2,最后到達 API。

返回:執(zhí)行完 API,再回到 AOP2,最后回到 AOP1。

如下圖:

iShot_2024-01-16_11.17.19.png

因為我們用的是 Around,先進入Aop1,再進入到aop2,然后執(zhí)行api,執(zhí)行完以后,再返回到 aop2,最后返回aop1。

3.2 代碼實現(xiàn)

MyFirstAop:

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

MyFirstAspect:

@Slf4j
@Aspect
@Component
@Order(100002)
public class MyFirstAspect {

    @Around("@annotation(aop)")
    public Object around(ProceedingJoinPoint joinPoint, 
                         MyFirstAop aop) throws Throwable {

        log.info("MyFirstAspect#around execute start");

        try {
            return joinPoint.proceed();
        } finally {
            log.info("MyFirstAspect#around execute end");
        }

    }

}

MySecondAop:

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

MySecondAspect:

@Slf4j
@Aspect
@Component
@Order(100003)
public class MySecondAspect {

    @Around("@annotation(aop)")
    public Object around(ProceedingJoinPoint joinPoint, 
                         MySecondAop aop) throws Throwable {

        log.info("MySecondAspect#around execute start");

        try {
            return joinPoint.proceed();
        } finally {
            log.info("MySecondAspect#around execute end");
        }

    }

}

SampleController#aopOrder:

@RestController
@RequestMapping("/sample")
public class SampleController {

    @MySecondAop
    @MyFirstAop
    @RequestMapping("/aop-order")
    public String aopOrder() {
        return "aopOrder";
    }

}
image-20240119075507098.png

通過設(shè)定 Order 值,指定 AOP 執(zhí)行順序,與我們的期望一致。

好了,今天的分享就到這里了,源碼:demo-spring-boot-aop。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容