?? 一、通知到 MethodInterceptor
的轉換機制
Spring AOP 通過適配器模式將開發者定義的注解型通知(如 @Before
)統一轉換為 MethodInterceptor
接口實現,確保所有通知類型能接入同一調用鏈。以下是轉換細節:
1. 適配器實現原理
- 核心接口:
MethodInterceptor
是所有通知的最終形態,其invoke(MethodInvocation mi)
方法封裝了通知邏輯與鏈式調用邏輯。 - 適配器作用:將不同通知類型(如
AspectJMethodBeforeAdvice
)包裝為MethodInterceptor
子類,實現邏輯轉換。
2. 各通知類型的轉換細節
下表總結了五種通知的轉換邏輯與執行順序:
通知類型 | 原始接口 | 轉換后實現 | invoke() 核心邏輯 |
---|---|---|---|
@Before | AspectJMethodBeforeAdvice | MethodBeforeAdviceInterceptor | 1. 執行 advice.before() 2. 調用 mi.proceed() 觸發后續鏈 |
@After | AspectJAfterAdvice | AfterAdviceInterceptor | 1. try { mi.proceed() } 2. finally { advice.after() } (確保最終執行) |
@AfterReturning | AspectJAfterReturningAdvice | AfterReturningAdviceInterceptor | 1. 調用 mi.proceed() 2. 若正常返回,執行 advice.afterReturning() |
@AfterThrowing | AspectJAfterThrowingAdvice | ThrowsAdviceInterceptor | 1. try { mi.proceed() } 2. catch(ex) { advice.afterThrowing(ex) } |
@Around | 無 | AspectJAroundAdvice | 1. 自定義前后邏輯 2. 通過 mi.proceed() 觸發后續鏈(需主動調用) |
關鍵設計:除
@Around
外,所有通知均被轉換為環繞通知形式,通過統一的invoke()
接口接入調用鏈。
🔄 二、MethodInvocation
與攔截器鏈的協同流程
1. 調用鏈構建過程
- 代理對象創建:Spring 容器啟動時,掃描所有
Advisor
(包含通知與切點)。 - 適配器轉換:通過
AdvisorAdapterRegistry
將Advisor
中的通知轉換為MethodInterceptor
。 - 鏈式存儲:生成
List<MethodInterceptor>
并注入ReflectiveMethodInvocation
。
2. 調用鏈執行邏輯(ReflectiveMethodInvocation.proceed()
)
public class ReflectiveMethodInvocation implements MethodInvocation {private final List<MethodInterceptor> interceptors;private int currentInterceptorIndex = -1; // 當前執行位置索引public Object proceed() throws Throwable {// 1. 所有攔截器執行完畢 → 反射調用目標方法if (this.currentInterceptorIndex == interceptors.size() - 1) {return invokeJoinpoint(); }// 2. 獲取下一個攔截器并推進索引MethodInterceptor interceptor = interceptors.get(++currentInterceptorIndex);// 3. 執行當前攔截器(觸發統一的invoke接口)return interceptor.invoke(this); // 將自身(MethodInvocation)傳入}
}
索引機制:
currentInterceptorIndex
記錄當前執行位置,每次proceed()
調用時遞增,實現攔截器的順序觸發。
3. 各通知在調用鏈中的協作順序
以下序列圖展示了典型調用流程(含多個通知類型):
協作關鍵:
- 嵌套執行:每個攔截器通過調用
mi.proceed()
觸發后續攔截器或目標方法,形成嵌套調用棧。- 邏輯控制權:攔截器可決定是否調用
proceed()
。例如權限校驗失敗時,Before
攔截器可不調用proceed()
,直接中斷鏈。
🧩 三、統一 MethodInterceptor
的設計價值
1. 外部易用性
開發者通過直觀注解(如 @Before
)聲明切面,無需理解底層調用鏈。適配器模式隱藏了復雜性,例如:
@Before("execution(* com.dwl.*.*(..))")
public void logBefore(JoinPoint jp) {System.out.println("Before: " + jp.getSignature());
}
2. 內部統一性
- 單一執行邏輯:
ReflectiveMethodInvocation.proceed()
只需遍歷List<MethodInterceptor>
,無需區分通知類型。 - 擴展性:新增通知類型時,只需實現適配器并注冊到
AdvisorAdapterRegistry
,無需修改調用鏈核心邏輯。
3. 異常處理優勢
- 統一異常傳播:異常沿調用鏈反向傳遞,由最近的
@AfterThrowing
或@Around
攔截器捕獲。 - 資源清理保證:
@After
邏輯在finally
塊執行,確保即使目標方法異常也能運行(如關閉數據庫連接)。
? 四、完整流程案例:日志切面執行
假設切面包含 @Before
、@Around
、@After
通知,調用鏈構建與執行如下:
-
轉換階段:
@Before
→MethodBeforeAdviceInterceptor
@Around
→AspectJAroundAdvice
@After
→AfterAdviceInterceptor
-
調用鏈執行順序:
-
異常場景:若目標方法拋出異常:
After
攔截器在finally
中執行日志清理。- 異常傳遞給Around攔截器,由其捕獲并記錄。
💎 總結
- 轉換必然性:適配器模式是 Spring AOP 的基石,將注解通知統一轉為
MethodInterceptor
,實現調用鏈標準化。 - 協同核心:
ReflectiveMethodInvocation
通過索引控制與嵌套調用(proceed()
)協調攔截器執行,形成責任鏈模式。 - 設計價值:
- 對外:簡化開發,通過注解屏蔽底層復雜度。
- 對內:通過統一接口減少冗余代碼,提升擴展性。
- 健壯性:異常處理與資源清理機制保障業務邏輯安全。
此設計完美體現了 “開閉原則”:新增通知類型無需修改調用鏈核心,僅需擴展適配器。