1. SpringAOP的基本概念
SpringAOP(Aspect-Oriented Programming)即面向切面編程,是Spring框架體系中非常重要的功能模塊之一。AOP與OOP(面向對象編程)相輔相成,提供了一種與OOP不同的抽象軟件結構的視圖。
在OOP中,程序的基本單元是類,而AOP的基本單元是Aspect(切面),切面能夠對那些影響多個類的公共行為進行封裝。這種“橫切”的技術,將封裝對象內部剖解開,將那些影響多個類的公共方法封裝到一個可重用的模塊中,從而減少系統的重復代碼,降低模塊間的耦合度,提高系統的可操作性和可維護性。
AOP采取橫向抽取機制,取代了傳統縱向繼承體系中的重復性代碼。在業務處理代碼中,如日志記錄、性能統計、安全控制、事務處理、異常處理等操作,盡管使用OOP可以通過封裝或繼承的方式達到代碼的重用,但仍然有相同的代碼分散在各個方法中。而AOP則能夠將這些操作封裝到獨立的模塊中,使關注點代碼與業務代碼分離,從而簡化了程序結構,提高了代碼的可讀性和可維護性。
AOP中的關鍵概念包括:
- Joinpoint(連接點):類中可以被增強的方法。
- Pointcut(切入點):實際被增強的方法,即在類中可以有很多方法被增強,但實際被增強的方法被稱為切入點。
- Advice(通知/增強):增強的邏輯,即具體的切面邏輯代碼。
總的來說,SpringAOP通過面向切面編程的方式,提供了一種靈活且強大的機制,用于處理那些跨越多個類和方法的公共行為,從而提高了軟件系統的可維護性和可擴展性。
AOP中的關鍵概念包括:
Joinpoint(連接點):類中可以被增強的方法。
Pointcut(切入點):實際被增強的方法,即在類中可以有很多方法被增強,但實際被增強的方法被稱為切入點。
Advice(通知/增強):增強的邏輯,即具體的切面邏輯代碼。
總的來說,SpringAOP通過面向切面編程的方式,提供了一種靈活且強大的機制,用于處理那些跨越多個類和方法的公共行為,從而提高了軟件系統的可維護性和可擴展性。
2.AOP代碼執行順序
在Spring AOP(Aspect-Oriented Programming)中,當使用環繞通知(Around Advice)時,代碼的執行順序遵循以下邏輯:
-
前置通知(Before Advice): 如果定義了前置通知,它會在目標方法執行之前被調用。前置通知主要用于執行一些準備性的工作,比如開啟事務、設置參數等。
-
環繞通知(Around Advice)開始: 環繞通知是AOP中最強大的通知類型,因為它允許你在目標方法執行前后進行自定義的邏輯處理。當目標方法被調用時,環繞通知的邏輯開始執行。
-
環繞通知內部邏輯: 在環繞通知中,你可以決定是否繼續執行目標方法,以及何時執行。這通過
ProceedingJoinPoint
的proceed()
方法來實現。如果你希望在目標方法執行前做一些處理,可以在調用proceed()
之前完成;如果需要在目標方法執行后做處理,可以在proceed()
之后完成。 -
目標方法執行: 當環繞通知中的
proceed()
方法被調用時,目標方法開始執行。這是實際業務邏輯的部分。 -
環繞通知后續邏輯: 在目標方法執行完畢后,環繞通知的后續邏輯將執行。這通常包括一些清理工作,比如關閉資源、提交或回滾事務等。
-
后置通知(After Advice): 如果定義了后置通知,它會在目標方法執行之后(無論目標方法是否成功執行)被調用。后置通知通常用于執行一些清理或日志記錄工作。
-
異常通知(After Throwing Advice): 如果目標方法拋出了異常,并且定義了異常通知,那么異常通知將在異常被拋出后執行。這允許你執行一些特定的異常處理邏輯。
-
返回通知(After Returning Advice): 如果定義了返回通知,并且目標方法成功返回了結果,那么返回通知將在目標方法返回結果之后執行。這通常用于處理方法的返回值。
總結來說,Spring AOP中的代碼執行順序是:前置通知 -> 環繞通知開始 -> 目標方法執行 -> 環繞通知后續邏輯 -> 后置通知/異常通知/返回通知。
注意:以上順序基于Spring AOP的默認行為,但可以通過配置或編程方式改變通知的執行順序。
3. SpringAop常用注解
Spring AOP(Aspect-Oriented Programming)常用的注解包括:
-
@Aspect
: 該注解用于聲明一個類為切面類,即這個類將包含一些通知(Advice)和切入點(Pointcut)的定義。 -
@Pointcut
: 該注解用于定義一個切入點,切入點是一個表達式,它指定了在哪些方法執行時要應用通知。切入點表達式通常基于方法簽名、異常類型、注解等來定義匹配規則。 -
@Before
: 該注解用于定義前置通知(Before Advice),即目標方法執行之前的通知。前置通知常用于執行一些準備性的工作,如設置參數、開啟事務、申請資源等。 -
@After
: 該注解用于定義后置通知(After Advice),即目標方法執行之后的通知。后置通知常用于執行一些清理工作,如關閉資源、記錄日志等。 -
@AfterReturning
: 該注解用于定義返回后通知(After Returning Advice),即目標方法正常返回之后的通知。這個通知可以獲取到目標方法的返回值,并進行相應的處理。 -
@AfterThrowing
: 該注解用于定義異常后通知(After Throwing Advice),即目標方法拋出異常之后的通知。這個通知可以在異常處理邏輯中發揮作用,如記錄異常信息、進行回滾操作等。 -
@Around
: 該注解用于定義環繞通知(Around Advice),即在目標方法執行前后都進行通知。環繞通知可以控制目標方法的執行流程,如是否執行目標方法、何時執行目標方法等。
為了使Spring AOP生效,你還需要在Spring配置中啟用AspectJ自動代理,這通常通過在配置類上添加@EnableAspectJAutoProxy
注解來實現。同時,你還需要確保切面類被Spring容器管理,通常通過在切面類上添加@Component
注解來實現。
4. 另外舉幾個例子說明
@Aspect
@Aspect
是 Spring AOP(Aspect-Oriented Programming)中的一個關鍵注解,用于定義一個切面(Aspect)。切面是 AOP 的核心概念之一,它代表了橫切關注點(cross-cutting concerns)的模塊化實現。這些關注點通常包括日志記錄、事務管理、權限驗證等,它們跨越多個應用層次和多個類,因此不適合直接放在業務邏輯代碼中。
@Aspect
注解通常標注在一個類上,該類隨后會被 Spring 容器識別為一個切面類,并且其中的通知(Advice)和切入點(Pointcut)會被容器自動處理。切面類可以包含多種類型的通知,如前置通知(@Before
)、后置通知(@After
)、返回后通知(@AfterReturning
)、異常后通知(@AfterThrowing
)以及環繞通知(@Around
)。
下面是一個簡單的例子,展示了如何使用 @Aspect
注解定義一個切面類:
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;@Aspect
@Component
public class LoggingAspect {// 定義一個切入點,匹配 com.example.service 包下的所有類的所有方法@Pointcut("execution(* com.example.service.*.*(..))")public void serviceMethods() {}// 使用 @Before 注解定義一個前置通知@Before("serviceMethods()")public void logBefore() {System.out.println("Method is being called");// 在這里可以添加日志記錄的邏輯}// 其他通知方法...
}
在這個例子中,LoggingAspect
類被標注為 @Aspect
,意味著它是一個切面類。該類定義了一個切入點 serviceMethods()
,它匹配 com.example.service
包下所有類的所有方法。然后,使用 @Before
注解定義了一個前置通知 logBefore()
,該通知會在匹配切入點的方法執行之前執行。
要使這個切面類生效,你需要在 Spring 配置中啟用 AspectJ 自動代理,這通常通過在配置類上添加 @EnableAspectJAutoProxy
注解來實現。此外,確保切面類被 Spring 容器管理,通常通過在類上添加 @Component
注解來實現。這樣,當目標方法被調用時,相應的通知就會根據切入點的規則被觸發執行。
@Pointcut
在Spring AOP(Aspect-Oriented Programming)中,@Pointcut
是一個注解,用于定義一個切入點(Pointcut)。切入點是一個表達式,它指定了在哪些方法執行時要應用通知(Advice)。簡而言之,@Pointcut
注解允許你定義一個規則,當這個規則匹配時,相應的通知(如前置通知、后置通知、異常通知等)就會被觸發。
@Pointcut
注解通常定義在一個方法上,這個方法沒有返回類型,并且通常沒有方法體(或者方法體為空)。這個方法的名稱代表了切入點的名稱,而方法的參數則是一個切入點表達式,用于指定哪些方法匹配該切入點。
切入點表達式通常使用AspectJ的表達式語言編寫,可以基于方法簽名(如方法名、參數類型、返回類型等)、異常類型、注解等來定義匹配規則。
下面是一個簡單的例子,展示了如何使用@Pointcut
注解來定義一個切入點:
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.annotation.Aspect;@Aspect
public class MyAspect {// 定義一個切入點,匹配com.example.service包下所有類的所有方法@Pointcut("execution(* com.example.service.*.*(..))")public void serviceMethods() {// 方法體為空,因為@Pointcut注解定義的是一個切入點規則,而不是具體的業務邏輯}// 其他通知(Advice)可以使用這個切入點// ...
}
在這個例子中,serviceMethods
方法定義了一個切入點,它匹配com.example.service
包下所有類的所有方法。之后,你可以在其他通知方法中使用serviceMethods()
作為切入點表達式,來指定這些通知應該在哪些方法執行時應用。
@Pointcut
注解使得切入點定義更加集中和易于管理,你可以在同一個Aspect類中定義多個切入點,然后在不同的通知中重復使用這些切入點。這有助于提高代碼的可讀性和可維護性。
@Before
在Spring AOP(Aspect-Oriented Programming)中,@Before
注解用于定義前置通知(Before Advice)。前置通知在目標方法執行之前執行,通常用于執行一些準備性的工作,如設置參數、開啟事務、申請資源等。
使用@Before
注解的方法通常會接收一個JoinPoint
參數,它代表了一個連接點(即一個方法的執行)。你可以通過這個參數獲取關于目標方法的信息,如方法名、參數等。
下面是一個使用@Before
注解的示例:
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;@Aspect
@Component
public class MyAspect {@Before("execution(* com.example.service.*.*(..))")public void logBeforeMethod(JoinPoint joinPoint) {System.out.println("About to execute method: " + joinPoint.getSignature().getName());// 這里可以添加一些準備性的工作,如開啟事務、申請資源等}
}
在這個示例中,@Before
注解定義了一個前置通知logBeforeMethod
,它會在com.example.service
包下所有類的所有方法執行之前執行。通知方法接收一個JoinPoint
參數,允許你獲取關于即將執行的方法的信息。
前置通知通常用于執行一些與目標方法執行相關的前置條件檢查或資源準備工作。例如,你可能需要在執行數據庫操作之前開啟事務,或者在調用某個服務之前申請必要的資源。
請注意,@Before
注解的切入點表達式決定了哪些方法執行前會觸發這個通知。在上面的示例中,切入點表達式execution(* com.example.service.*.*(..))
意味著所有在com.example.service
包下的類的所有方法執行前都會觸發這個通知。
最后,和@After
注解一樣,你需要在Spring配置中啟用AspectJ自動代理,以便Spring能夠識別和處理這些方面(Aspects)。這通常通過在配置類上添加@EnableAspectJAutoProxy
注解來實現。
@After
在Spring AOP(Aspect-Oriented Programming)中,@After
注解用于定義后置通知(After Advice)。后置通知在目標方法執行完畢之后執行,無論目標方法是否成功完成(即無論是否拋出異常)。這意味著,即使目標方法拋出異常,后置通知也會被觸發。
使用@After
注解的方法通常會接收一個JoinPoint
參數,它代表了一個連接點(即一個方法的執行)。然而,@After
通知通常不需要這個參數,因為它是在目標方法執行完畢之后運行的,此時你已經無法影響目標方法的執行了。
下面是一個使用@After
注解的示例:
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;@Aspect
@Component
public class LoggingAspect {@After("execution(* com.example.service.*.*(..))")public void logAfterMethod() {System.out.println("Method has been executed.");}
}
在這個示例中,@After
注解定義了一個后置通知logAfterMethod
,它會在com.example.service
包下所有類的所有方法執行完畢后執行。通知方法沒有接收任何參數,因為它只是簡單地打印一條消息,表明方法已經執行完畢。
請注意,@After
注解的切入點表達式決定了哪些方法執行后會觸發這個通知。在上面的示例中,切入點表達式execution(* com.example.service.*.*(..))
意味著所有在com.example.service
包下的類的所有方法執行后都會觸發這個通知。
后置通知通常用于執行一些清理工作、日志記錄或監視任務,這些任務需要在方法執行后但不需要考慮方法是否成功執行的情況下執行。
最后,請記住要在Spring配置中啟用AspectJ自動代理,以便Spring能夠識別和處理這些方面(Aspects)。這通常通過在配置類上添加@EnableAspectJAutoProxy
注解來實現。
@AfterReturning
@AfterReturning
是 Spring AOP(Aspect-Oriented Programming)中的一個注解,用于定義一個返回后通知(After Returning Advice)。這種通知會在被切點方法正常執行并返回之后執行,它允許你訪問到被切點方法的返回值,并根據這個返回值進行一些后續的處理操作。
這個注解通常用于執行一些在方法執行成功后的邏輯,比如根據方法的返回結果更新某些狀態、記錄日志、發送通知等。
@AfterReturning
注解有幾個重要的屬性:
pointcut
或value
:用于指定切入點表達式,確定哪些方法的執行會觸發這個通知。returning
:指定一個參數名,這個參數會接收被切點方法的返回值,你可以在通知方法中通過這個參數訪問到返回值。
下面是一個使用 @AfterReturning
的例子:
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;@Aspect
@Component
public class LoggingAspect {// 定義一個切入點,匹配 com.example.service 包下的所有類的所有方法@Pointcut("execution(* com.example.service.*.*(..))")public void serviceMethods() {}// 使用 @AfterReturning 注解定義一個返回后通知@AfterReturning(pointcut = "serviceMethods()", returning = "result")public void logAfterReturning(JoinPoint joinPoint, Object result) {System.out.println("Method executed successfully: " + joinPoint.getSignature().getName());System.out.println("Returned value: " + result);// 在這里可以根據返回值進行一些處理,比如記錄日志、更新狀態等}
}
在這個例子中,logAfterReturning
方法是一個返回后通知,它會在 serviceMethods()
切入點匹配的方法成功執行后執行。通過 returning = "result"
指定了一個參數名 result
,這個參數會接收被切點方法的返回值,然后在通知方法中打印出來。
要使這個切面類生效,你需要在 Spring 配置中啟用 AspectJ 自動代理,并確保切面類被 Spring 容器管理。這樣,當目標方法成功執行并返回后,相應的返回后通知就會根據切入點的規則被觸發執行。
@AfterThrowing
@AfterThrowing
是 Spring AOP(Aspect-Oriented Programming)中的一個注解,用于定義一個異常后通知(After Throwing Advice)。這個通知在被切點方法拋出異常時執行,允許你在方法發生異常時執行一些邏輯,如記錄異常信息、進行清理工作、回滾事務等。
@AfterThrowing
注解有幾個重要的屬性:
pointcut
或value
:用于指定切入點表達式,確定哪些方法的異常會觸發這個通知。throwing
:指定一個參數名,該參數將接收被切點方法拋出的異常對象,允許你在通知方法中訪問并處理這個異常。
下面是一個使用 @AfterThrowing
的例子:
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;@Aspect
@Component
public class ExceptionHandlingAspect {// 定義一個切入點,匹配 com.example.service 包下的所有類的所有方法@Pointcut("execution(* com.example.service.*.*(..))")public void serviceMethods() {}// 使用 @AfterThrowing 注解定義一個異常后通知@AfterThrowing(pointcut = "serviceMethods()", throwing = "ex")public void handleException(JoinPoint joinPoint, Throwable ex) {System.out.println("Method failed: " + joinPoint.getSignature().getName());System.out.println("Thrown exception: " + ex.getMessage());// 在這里可以記錄異常信息、進行清理工作、回滾事務等}
}
在這個例子中,handleException
方法是一個異常后通知,它會在 serviceMethods()
切入點匹配的方法拋出異常時執行。通過 throwing = "ex"
指定了一個參數名 ex
,這個參數會接收被切點方法拋出的異常對象,然后在通知方法中打印出異常信息。
要使這個切面類生效,你需要在 Spring 配置中啟用 AspectJ 自動代理,并確保切面類被 Spring 容器管理。這樣,當目標方法拋出異常時,相應的異常后通知就會根據切入點的規則被觸發執行。
通過結合使用 @Pointcut
來定義復用的切入點表達式,你可以使通知的定義更加簡潔和可維護。同時,Spring AOP 還支持其他類型的通知,如 @Before
(前置通知)、@After
(后置通知)和 @Around
(環繞通知),它們提供了在方法執行的不同階段執行自定義邏輯的能力。
@Around
在Spring AOP中,@Around注解用于定義環繞通知(Around Advice),它是最強大的一種通知類型,因為它允許你在目標方法執行前后進行自定義的邏輯處理,并且可以決定是否繼續執行目標方法以及何時執行。
當你使用@Around注解時,你需要定義一個方法,這個方法將接收一個ProceedingJoinPoint參數,這個參數代表了被通知方法的執行點。你可以在這個方法內部實現自己的邏輯,比如前置處理、后置處理、異常處理等。
下面是一個使用@Around注解的示例:
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component; @Aspect
@Component
public class MyAspect { @Around("execution(* com.example.service.*.*(..))") public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable { long start = System.currentTimeMillis(); Object proceed = joinPoint.proceed(); // 繼續執行目標方法 long executionTime = System.currentTimeMillis() - start; System.out.println(joinPoint.getSignature() + " executed in " + executionTime + "ms"); return proceed; }
}
在這個示例中,@Around注解用于定義一個環繞通知,它會在com.example.service包下所有類的所有方法執行前后起作用。通知方法logExecutionTime接收一個ProceedingJoinPoint參數,允許你在執行目標方法之前和之后插入自定義邏輯。
在logExecutionTime方法中:
記錄方法開始執行的時間。
調用proceed()方法執行目標方法。
記錄方法執行結束的時間,并計算執行時長。
打印方法的執行時長。
如果proceed()方法沒有被調用,那么目標方法將不會被執行。這意味著你可以在環繞通知中根據某些條件決定是否繼續執行目標方法。
請注意,為了使用@Around注解,你需要在Spring配置中啟用AspectJ自動代理,這通常通過在配置類上添加@EnableAspectJAutoProxy注解來實現。
此外,@Around注解中的切入點表達式(Pointcut Expression)決定了哪些方法會被這個通知所影響。在上面的示例中,切入點表達式execution(* com.example.service..(…))意味著所有在com.example.service包下的類的所有方法都會被這個通知所影響。你可以根據需要調整切入點表達式以匹配特定的方法。
暫時寫到這里,以后會接著更新文檔