首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Spring AOP 核心機制深度解析:攔截器鏈的調用實現原理

     編輯:Java 相關知識

前言:從 Proxy 到攔截器鏈的完整鏈路

在前文中我們已經分析了 Spring AOP 如何生成 Proxy 代理對象,今天我們將深入探討攔截器鏈是如何被調用的——這正是 Proxy 模式發揮作用的核心環節,也是 Spring 為我們提供 AOP 功能的關鍵實現路徑。

JDK 動態代理的回調入口:InvocationHandler.invoke()

在 JdkDynamicAopProxy 中生成 Proxy 實例時,核心代碼如下:

return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);

這裡的 this 參數對應的是 InvocationHandler 實例,而 JdkDynamicAopProxy 正好實現了該接口。因此,當代理對象的任何方法被調用時,都會回調到 invoke(Object proxy, Method method, Object[] args) 方法。這就是整個 AOP 攔截鏈啟動的起點。

invoke() 方法核心邏輯深度剖析

該方法的主要職責包括:處理特殊方法(equals/hashCode)、管理 Proxy 上下文、獲取目標對象、構建攔截器鏈、決定是直接反射調用還是走攔截器鏈、處理返回值、最後釋放資源。以下是關鍵步驟解讀:

1. 特殊方法快速路徑

對於 equals()、hashCode() 以及 Advised 接口定義的方法,Spring 提供了直接處理邏輯,避免進入攔截器鏈,提升性能。

2. Proxy 上下文暴露

若 exposeProxy 為 true,則通過 AopContext.setCurrentProxy(proxy) 將當前代理綁定到線程局部變量,方便目標方法內部獲取代理引用(例如自我調用場景)。

3. 目標對象獲取與攔截器鏈構建

TargetSource targetSource = this.advised.targetSource;
Object target = targetSource.getTarget(); // 可能來自對象池
Class targetClass = (target != null) ? target.getClass() : null;

// 核心:獲取針對當前方法的攔截器鏈
List chain = this.advised.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
    this.advised, proxy, method, targetClass);

這裡的 chain 包含了所有匹配當前方法的攔截器(包括動態方法匹配的 Advisor)。

4. 分發決策:無攔截器直連 vs 有攔截器進鏈

  • 鏈為空:直接通過反射調用目標方法 AopUtils.invokeJoinpointUsingReflection(target, method, args)。
  • 鏈不為空:構造 ReflectiveMethodInvocation 並調用其 proceed() 方法,正式進入攔截器鏈執行流程。

5. 返回值修正與資源清理

若目標方法返回 this 且返回類型兼容代理類型,則將返回值替換為代理對象,保證調用端拿到的是代理而非裸目標對象。最後在 finally 塊中釋放目標對象(若來自池)並恢復舊的 Proxy 上下文。

反射調用目標方法的工具類:AopUtils.invokeJoinpointUsingReflection

這是一個純反射調用封裝,核心邏輯:

  • 若方法或聲明類非 public,自動 setAccessible(true) 破壞封裝。
  • 捕獲 InvocationTargetException 並拋出真實異常 ex.getTargetException(),保證異常棧不被攔截器污染。
  • 將非法參數/訪問異常轉換為 Spring 統一的 AopInvocationException。

攔截器鏈的核心執行引擎:ReflectiveMethodInvocation.proceed()

這是整個 AOP 執行流程的「心臟」。它維護一個 currentInterceptorIndex 指針,每次 proceed() 都會:

  1. 若指針已達鏈尾(index == size),調用 invokeJoinpoint() 執行真實目標方法。
  2. 否則取出當前攔截器:
    • 若是 InterceptorAndDynamicMethodMatcher,執行動態匹配,匹配成功則調用 dm.interceptor.invoke(nextInvocation()),失敗則 index++ 遞歸 proceed()。
    • 若是普通 MethodInterceptor,直接調用 interceptor.invoke(nextInvocation())。

關鍵設計:nextInvocation() 的 Clone 機制

private ReflectiveMethodInvocation nextInvocation() throws CloneNotSupportedException {
    ReflectiveMethodInvocation invocation = (ReflectiveMethodInvocation) clone();
    invocation.currentInterceptorIndex = this.currentInterceptorIndex + 1;
    invocation.parent = this;
    return invocation;
}

每次進入下一個攔截器時,都會 克隆當前調用上下文 並遞增索引。這種不可變鏈式設計保證了:

  • 每個攔截器看到的都是自己的「視角」(索引已指向下一個)。
  • 攔截器內部調用 invocation.proceed() 時,實際執行的是克隆對象的 proceed(),從而自然串聯起整條鏈。
  • 支持巢狀事務等場景下的狀態隔離(如 TransactionInterceptor 中多次 proceed())。

實戰驗證:TransactionInterceptor 中的雙重 proceed()

在 TransactionInterceptor.invoke() 中可以看到典型模式:

try {
    retVal = invocation.proceed(); // 執行後續攔截器 + 目標方法
} catch (Throwable ex) {
    completeTransactionAfterThrowing(txInfo, ex);
    throw ex;
} finally {
    cleanupTransactionInfo(txInfo);
}
commitTransactionAfterReturning(txInfo);

若事務管理器實現了 CallbackPreferringPlatformTransactionManager,則改用模板回調風格,但本質仍是通過 invocation.proceed() 驅動鏈條向前流動。

總結

Spring AOP 的攔截器調用機制展示了經典的 責任鏈模式 + 命令模式 + 原型模式(Clone) 的組合應用:

  • JdkDynamicAopProxy.invoke() 是入口,負責上下文準備與分發決策。
  • ReflectiveMethodInvocation 封裝了「調用上下文 + 鏈狀結構 + 索引指針」,是鏈條執行的核心載體。
  • nextInvocation() 通過淺拷貝實現狀態遞進,巧妙解決了遞歸調用時的狀態隔離問題。
  • 攔截器只需關注 MethodInvocation.proceed(),無感知鏈條其餘細節,實現了極佳的解耦。

理解這套機制,不僅有助於排查 AOP 不生效、執行順序錯誤等疑難雜症,更能啟發我們在設計插件系統、中間件鏈路時的架構思維。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved