博客 / 詳情

返回

Spring Boot AOP - 面向切面編程

什麼是AOP

在Spring Boot AOP中,非核心業務功能被定義為切面,核心和非核心功能都開發完成之後,再將兩者編織在一起,這就是AOP。

舉個例子,假設你的應用程序需要記錄每次方法調用的開始時間和結束時間。如果你不使用AOP,你可能需要在每個方法的開頭和結尾都寫一段代碼來記錄時間。但是,這樣的做法既繁瑣又容易遺漏。
使用AOP,你可以創建一個"時間記錄"的切面,告訴系統在每個方法執行前後都執行一段代碼來記錄時間。這樣,你就可以把時間記錄這個關注點從業務邏輯中剝離出來,讓每個方法專注於自己的核心任務,而不必擔心時間記錄。

AOP中的編程術語和常用註解

切面:非核心業務功能就被定義為切面。比如一個系統的日誌功能,它貫穿整個核心業務的邏輯,因此叫做切面
切入點:在哪些類的哪些方法上面切入
通知:在方法執行前/後或者執行前後做什麼

前置通知:在被代理方法之前執行
後置通知:在被代理方法之後執行
返回通知:被代理方法正常返回之後執行
異常通知:被代理方法拋出異常時執行
環繞通知:是AOP中強大、靈活的通知,集成前置和後置通知

springboot常見的使用註解

@Pointcut 定義切點
@Before 前置通知
@After 後置通知
@AfterReturning 返回通知
@AfterThrowing 異常通知
@Around 環繞通知

切點常用寫法

1)execution(public * (..))——表示匹配所有public方法
2)execution( set(..))——表示所有以“set”開頭的方法
3)execution( com.example.controller.TestController.(..))——表示匹配所有TestController接口的方法
4)execution( com.example.controler..(..))——表示匹配controler包下所有的方法
5)execution(* com.example.service...(..))——表示匹配controler包和它的子包下的方法

JoinPoint 對象

JoinPoint對象封裝了SpringAop中切面方法的信息,在切面方法中添加JoinPoint參數,就可以獲取到封裝了該方法信息的JoinPoint對象.

方法名    功能
Signature getSignature();    獲取封裝了署名信息的對象,在該對象中可以獲取到目標方法名,所屬類的Class等信息
Object[] getArgs();    獲取傳入目標方法的參數對象
Object getTarget();    獲取被代理的對象
Object getThis();    獲取代理對象

ProceedingJoinPoint對象

ProceedingJoinPoint對象是JoinPoint的子接口,該對象只用在@Around的切面方法中, 
Object proceed() throws Throwable //執行目標方法 
Object proceed(Object[] var1) throws Throwable //傳入的新的參數去執行目標方法 

使用aop,第一步導入maven dependency

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

編寫Controller類

@RestController
@RequestMapping("/test")
public class TestController {

    @GetMapping("hello")
    public String sayHello(){
        System.out.println("hello");
        return "hello";
    }
}

定義切面

@Aspect
@Component
public class TestAspect {

    @Pointcut("execution(* com.example.demo.controller.TestController.sayHello())")
    private void serviceMethods() {}

    // 前置通知,在目標方法執行前執行
    @Before("serviceMethods()")
    public void beforeAdvice(JoinPoint joinPoint) {
        System.out.println("Before executing: " + joinPoint.getSignature().toShortString());
    }

    // 後置通知,在目標方法執行後執行
    @After("serviceMethods()")
    public void afterAdvice(JoinPoint joinPoint) {
        System.out.println("After executing: " + joinPoint.getSignature().toShortString());
    }

    // 返回通知,在目標方法成功返回後執行
    @AfterReturning(pointcut = "serviceMethods()", returning = "result")
    public void afterReturningAdvice(JoinPoint joinPoint, Object result) {
        System.out.println("After returning from: " + joinPoint.getSignature().toShortString());
        System.out.println("Result: " + result);
    }

    // 異常通知,在目標方法拋出異常時執行
    @AfterThrowing(pointcut = "serviceMethods()", throwing = "exception")
    public void afterThrowingAdvice(JoinPoint joinPoint, Exception exception) {
        System.out.println("Exception thrown from: " + joinPoint.getSignature().toShortString());
        System.out.println("Exception: " + exception.getMessage());
    }

    // 環繞通知,在目標方法執行前後都可以插入邏輯
    @Around("serviceMethods()")
    public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("Around - Before executing: " + proceedingJoinPoint.getSignature().toShortString());

        // 執行目標方法
        Object result = proceedingJoinPoint.proceed();

        System.out.println("Around - After executing: " + proceedingJoinPoint.getSignature().toShortString());

        return result;
    }

}

切面類需要打上@Aspect註解表示這是一個切面類,並且加上@Component註解

首先是定義切點,只需定義一個方法,在上面使用@Pointcut註解即可,註解裏面內容含義如下:

execution 代表方法被執行時觸發
* 代表任意返回值的方法
com.example.demo.controller.TestController.sayHello(..)被織入類的全限定名
(..) 表示任意的參數

發起一個請求

1703321261010_FCCAE851-6021-4d7d-9108-BBBA759F378E.png

通過這個,我們也可以發現各個通知的執行順序,由於這個案例使用@Around的時候,先執行了一步,之後在執行其他的通知

Around -> Before ->  AfterReturning -> After -> Around

總結

AOP其實使用起來是個很方便的東西,大大降低了相關功能之間的耦合度,使得整個系統井井有條。

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.