什麼是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(..)被織入類的全限定名
(..) 表示任意的參數
發起一個請求
通過這個,我們也可以發現各個通知的執行順序,由於這個案例使用@Around的時候,先執行了一步,之後在執行其他的通知
Around -> Before -> AfterReturning -> After -> Around
總結
AOP其實使用起來是個很方便的東西,大大降低了相關功能之間的耦合度,使得整個系統井井有條。