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

Spring AOP 代理創建全流程源碼分析:從 ProxyFactoryBean 到 JDK/CGLIB 雙引擎實現

     編輯:Java 相關知識

核心概念速覽:Advice、Pointcut、Advisor 三劍客

在深入代碼前,先建立統一術語:

  • Advice(通知):定義「何時」做「什麼」——MethodBeforeAdvice、AfterReturningAdvice、ThrowsAdvice 等接口。
  • Pointcut(切點):定義「在哪裡」應用通知——如 JdkRegexpMethodPointcut 通過正則匹配方法名。
  • Advisor(通知器):將 Advice 與 Pointcut 綁定——DefaultPointcutAdvisor 持有 advice 和 pointcut 兩個屬性。

ProxyFactoryBean:代理對象的工廠入口

ProxyFactoryBean 實現了 FactoryBean,其 getObject() 返回代理實例:

public Object getObject() throws BeansException {
    initializeAdvisorChain(); // 解析 interceptorNames 構建 Advisor 鏈
    return isSingleton() ? getSingletonInstance() : newPrototypeInstance();
}

單例代理創建關鍵路徑

private synchronized Object getSingletonInstance() {
    if (singletonInstance == null) {
        targetSource = freshTargetSource();
        if (autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {
            setInterfaces(ClassUtils.getAllInterfacesForClass(targetSource.getTargetClass()));
        }
        super.setFrozen(freezeProxy); // 凍結配置,禁止運行時修改
        singletonInstance = getProxy(createAopProxy()); // 核心:創建 AopProxy 再獲取代理
        addListener(this); // 監聽 Advisor 變更以重建單例
    }
    return singletonInstance;
}

AopProxy 抽象與 DefaultAopProxyFactory:雙引擎選型邏輯

Spring 通過 AopProxy 接口屏蔽具體代理實現,DefaultAopProxyFactory 負責決策:

public AopProxy createAopProxy(AdvisedSupport advised) throws AopConfigException {
    // 優先使用 CGLIB 的條件
    if (advised.isOptimize() || advised.isProxyTargetClass() || advised.getProxiedInterfaces().length == 0) {
        if (!cglibAvailable) throw new AopConfigException("CGLIB2 not available...");
        return CglibProxyFactory.createCglibProxy(advised);
    } else {
        return new JdkDynamicAopProxy(advised); // 默認 JDK 動態代理
    }
}

決策矩陣:

場景代理類型觸發條件
目標實現了接口,無特殊配置JDK Dynamic Proxy默認
目標未實現接口CGLIBproxiedInterfaces.length == 0
強制代理目標類CGLIBproxyTargetClass=true
優化模式CGLIBoptimize=true

JDK 動態代理實現:JdkDynamicAopProxy

public Object getProxy(ClassLoader classLoader) {
    Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
    findDefinedEqualsAndHashCodeMethods(proxiedInterfaces); // 處理 equals/hashCode
    return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); // this = InvocationHandler
}

關鍵點:

  • 實現 InvocationHandler,所有代理方法調用轉發到 invoke()(見第 1 篇文章詳細分析)。
  • completeProxiedInterfaces 會自動補全 SpringProxy、Advised、DecoratingProxy 等標記接口。

CGLIB 代理實現:Cglib2AopProxy(舊版本) / ObjenesisCglibAopProxy(新版本)

核心邏輯:

  • 使用 Enhancer 創建目標類的子類。
  • 設置 CallbackFilter 區分不同方法的攔截策略(如 equals/hashCode 走 FixedValue,業務方法走 MethodInterceptor)。
  • 通過 Objenesis 繞過構造函數實例化代理類(避免目標類無無參構造函數報錯)。
  • 設置 FastClass 機制加速方法調用(索引直達,避免反射開銷)。

AdvisedSupport:配置模型的核心持有者

ProxyFactoryBean 繼承自 AdvisedSupport,後者維護:

  • advisorChain:List 有序攔截器鏈。
  • targetSource:TargetSource 目標對象提供者(支援單例、原型、池化、熱換)。
  • interfaces:代理需實現的接口數組。
  • proxyTargetClass/optimize:代理策略標記。
  • frozen:配置凍結標記。

所有 AOP 配置最終都匯聚到此對象,JdkDynamicAopProxy 和 CglibAopProxy 均以此為輸入構建代理。

TargetSource 體系:目標對象的多態獲取策略

  • SingletonTargetSource:單例模式,持有固定目標實例。
  • PrototypeTargetSource:每次調用 getTarget() 從 BeanFactory 獲取新實例。
  • PoolingTargetSource:對象池模式,getTarget() 借對象,releaseTarget() 還對象。
  • HotSwappableTargetSource:運行時熱換目標對象,無需重建代理。

這正是 Spring AOP 支援「目標對象生命週期與代理生命週期解耦」的關鍵設計。

總結:代理創建的完整調用鏈

  1. ProxyFactoryBean.getObject() → initializeAdvisorChain() 解析配置構建 Advisor 鏈。
  2. getSingletonInstance() → createAopProxy() → DefaultAopProxyFactory.createAopProxy() 選型。
  3. JDK 路徑:new JdkDynamicAopProxy(advised) → Proxy.newProxyInstance(..., this)。
  4. CGLIB 路徑:Enhancer.create() + CallbackFilter + MethodInterceptor。
  5. 代理實例持有 AdvisedSupport 引用,方法調用轉發到統一攔截入口(JDK 為 InvocationHandler.invoke,CGLIB 為 MethodInterceptor.intercept)。
  6. 攔截入口委託 ReflectiveMethodInvocation / CglibMethodInvocation 執行責任鏈。

理解這條鏈路,能幫助你快速定位:為什麼代理沒生效?為什麼報 AopConfigException?為什麼 final 方法不被攔截?為什麼自我調用失效?——所有答案都在這條創建與調用鏈中。

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