Xuewei HUANG

Xuewei's Blog

工欲善其事,必先利其器.
github

Spring AOP 原理分析

TL;DR#

image_1723722735889_0 image_1723722759799_0 Spring AOP 拦截器链执行
  • @EnableAspectJAutoProxy 開啟自動代理
    • Context 啟動會創建 AnnotationAwareAspectJAutoProxyCreator 的 BeanDefinition 放到「BeanFactory」中的「beanDefinitionMap」
    • 創建的 bean 實例放到「BeanFactory」中的「singletonObjects」和「beanPostProcessors」
  • 前置處理,掃描所有 Aspect,生成所有的 Advisor,放入快取
    • 在創建 Bean 流程中的「實例化 Bean 的前置處理階段」,掃描所有 @Aspect 修飾的類,生成「Advisor (Advice + Pointcut)」列表,並快取 advisorsCahce 中
  • 創建 Proxy Bean (JDK Proxy 或 cglib 實現)
    • 一般情況,在創建 Bean 流程中的「初始化 bean 的後置處理階段」,但是在「循環依賴」情況下,會提前 AOP,在「填充 bean 屬性階段」,提前創建 Proxy Bean
    • 檢查 Advisor 的 Pointcut 條件是否與 bean 對應類中任一方法「匹配」,存在匹配情況,若匹配放入 「eligibleAdvisors」 列表
    • 若「eligibleAdvisors」列表不為空,說明需要 AOP,進而創建 Proxy Bean,放入「BeanFactory」
      • ProxyBean -> AopProxy (JDK or Cglib) -> ProxyFacotry (advised) -> eligibleAdvisors
  • Proxy Bean 調用代理方法,調用「攔截器鏈」鏈式執行「Advice」的方法
    • 獲取「Target Bean」和被調用方法所匹配的「攔截器鏈 (Advisor <-> MethodInterceptor)」
    • 通過創建 ReflectiveMethodInvocation 實例,遞歸調用 proceed 方法,遍歷「攔截器鏈」,進行反射調用「攔截器」的 Advice 方法
    • ReflectiveMethodInvocation 實例通過維護 currentInterceptorIndex 索引來遍歷「攔截器鏈」,當遍歷完「攔截器鏈」會調用 invokeJoinpoint 方法,調用「Target Bean」的目標方法。

什麼是 AOP?#

  • AOP (Aspect Oriented Programming, 面向切面編程) 是一種「編程範式」
  • 本質是將「核心功能」與「切面功能」進行「解耦」,可分別獨立進行開發功,然後將「核心功能」和「切面功能」進行「織入 Weaving」

什麼是 Spring AOP?#

  • 結合 Spring IOC 容器,通過在創建 Bean 過程中進行增強,生成 Proxy Bean,實現在程序 Runtime 期間,「動態」地將「核心功能」和「切面功能」進行「織入 Weaving」
  • 本質就是,通過「代理模式 (動態代理)」實現 AOP,並結合 Spring IOC 容器,存放 Proxy Bean

相關概念#

  • Jointpoint: 連接點 (方法執行,異常處理)
  • Pointcut: 切點
  • Advice: 在 Jointpoint 執行的「切面功能」操作

    也叫「通知」,包括 Around, Before, After, AfterReturning, AfterThrowing

  • Advisor: Pointcut + Advice

Spring AOP 的工作流程#

  1. 開啟 AspectJ 註解自動代理

    生成 AnnotationAwareAspectJAutoProxyCreator 的 bean 實例,放入「BeanFactory」中的「beanDefinitionMap」,「singletonObjects」和「beanPostProcessors」

    Spring_AOP_注解自动代理_1723188520212_0

    • Spring AOP 開啟 AspectJ 自動代理對應的註解是 @EnableAspectJAutoProxy,通過註解 @Import 導入 AspectJAutoProxyRegistrar
    • AspectJAutoProxyRegistrar 作用是將 AnnotationAwareAspectJAutoProxyCreator 生成對應的 BeanDefinition 對象,放入到 BeanDefinition 註冊中心中
    • AnnotationAwareAspectJAutoProxyCreator
      • 根據 BeanDefinition 創建 AnnotationAwareAspectJAutoProxyCreator 的 Bean 對象,並加入到 BeanFactory 的 beanPostProcessors (Bean 處理器列表) 中
      • 實現了 SmartInstantiationAwareBeanPostProcessor 接口,重要的方法如下
        1. Bean 初始化前置處理方法: postProcessBeforeInitialization()
        2. 獲取早期 Bean 引用: getEarlyBeanReference()
        3. Bean 初始化後置處理方法: postProcessAfterInitialization()
  2. 前置準備

    查找所有「切面 Aspect」類來生成「Advisor (Advice + Pointcut)」列表,並快取起來

    Spring_AOP_前置处理_1723304931489_0

    • 在創建 Bean 的流程,在「實例化 Bean」之前的階段 「resolveBeforeInstantiation」
    • 在第一次調用 AnnotationAwareAspectJAutoProxyCreator 的 bean 實例的postProcessBeforeInstantiation 方法
      • 從「BeanFacotry」中獲取所有的 Bean Name,遍歷 Bean Name 列表,根據 beanName 查詢「beanDefinitonMap」對應的類,是否含有 @Aspect 註解
      • 對含有 @Aspect 的類,掃描其所有的「Advice」方法和對應的「Pointcut」方法,生成「Advisor」列表
      • 將「Advisor」列表快取到 advisorCache Map 中
  3. 創建 Proxy Bean

    若在創建 Bean 過程,與「Advisor」集合任意匹配成功,創建 AOP 代理對象

    Spring_AOP_在初始化_bean_後置處理創建_Proxy_Bean_1723722535436_0
    • 一般情況,進行 AOP 創建 Proxy Bean,是在「初始化 bean 後置處理階段」

      • 在創建 bean 流程的「初始化 bean 後置處理階段」
      • 調用 AnnotationAwareAspectJAutoProxyCreator 的 bean 實例的 postProcessAfterInitialization 方法,調用 wrapIfNecessary 方法創建 Proxy Bean
      • 從「advisorsCache」獲取所有的 Advisor,通過查看當前需要創建的 bean 的類中,是否有任一方法與「Advisor 的 Pointcut 條件」匹配,從而篩選出匹配的「eligibleAdvisors」列表
      • 創建 ProxyFactory 實例
        • 將「匹配 advisor 列表」設置到 ProxyFactory 的「advisors」屬性中
        • 將包裝了「實例化的 bean」的 SingletonTargetSource 實例設置到 ProxyFactory 的「targetSource」屬性中
      • 調用 ProxyFactory 實例的 createAopProxy 方法,根據條件選擇創建 AopProxy 實例
        • 「JdkDynamicAopProxy」
        • 「ObjenesisCglibAopProxy」
      • 調用 Aop Proxy 實例的 getProxy 方法的實現 (JDK 或 Cglib),創建「Proxy Bean」
      • 將 Proxy Bean 放入到「BeanFactory 容器」的「singletonObjects」中
    • 在有「循環依賴」下,需提前進行 AOP,提前創建 Proxy Bean,是在「填充 bean 屬性階段」
      e96d64ab-3463-4e7a-839b-e1b91b276400

      Mermaid Loading...
      • 在創建 bean 流程的「填充 bean 屬性階段」
      • 調用 AnnotationAwareAspectJAutoProxyCreator 的 bean 實例的 getEarlyBeanReference 方法,進行提前 AOP,調用 wrapIfNecessary 方法創建 Proxy Bean
      • 創建 Proxy Bean 與一般情況一樣,詳情看上述一般情況
  4. Proxy Bean 調用代理方法,調用「攔截器鏈」鏈式執行「Advice」方法
    Spring AOP 拦截器链执行

    • 調用 Proxy Bean 實例的方法,調用代理方法
      • JDK 代理,調用的是 JdkDynamicAopProxy (InvocationHandler) 的 invoke 方法
      • Cglib 代理,調用的是 ObjenesisCglibAopProxy 的 intercept 方法
    • 從 advised (即 ProxyFactory) 中取出「原始 Bean (Target Bean)」
    • 構建與「被調用方法 callee method」所匹配的「攔截器鏈」
      • 從 advised (即 ProxyFactory) 中的與 bean 的類所匹配的所有的 「Advisor」
      • 然後遍歷檢查「Advisor」是否與當前方法匹配
      • 與當前方法匹配的「Advisor」,進行構建出對應的「Interceptor 攔截器」,放入「攔截器鏈」列表中
      • 快取和返回「攔截器鏈」
    • 構建 MethodInvocation,包裝 Proxy Bean, Target Bean, 「攔截器鏈」等,執行「攔截器鏈」
      • 根據不同代理創建不同的  MethodInvocation,遞歸調用 proceed 方法
        • JDK 代理,創建 ReflectiveMethodInvocation,進行遞歸調用 ReflectiveMethodInvocation 的 proceed 方法
        • Cglib 代理,創建 CglibMethodInvocation,進行遞歸調用 ReflectiveMethodInvocatio 的 proceed 方法 (CglibMethodInvocation 繼承了 ReflectiveMethodInvocatio)
      • 通過反射調用「攔截器」的 Advice 方法
      • ReflectiveMethodInvocation 實例通過維護 currentInterceptorIndex 索引來遍歷「攔截器鏈」
      • 如果已執行完「攔截器鏈」,調用 ReflectiveMethodInvocatio 的 invokeJoinpoint 方法,反射調用「原始 Bean (Target Bean)」的方法 (被代理)

攔截器與 Advice 和註解的關係#

  • @Around
    • Advice 和攔截器: AspectJAroundAdvice
  • @Before
    • 適配器: MethodBeforeAdviceAdapter
    • Advice: AspectJMethodBeforeAdvice->MethodBeforeAdvice
    • 攔截器: MethodBeforeAdviceInterceptor
  • @After
    • Advice 和攔截器: AspectJAfterAdvice
  • @AfterReturning
    • 適配器: MethodBeforeAdviceAdapter
    • Advice: AspectJAfterReturningAdvice->AfterReturningAdvice
    • 攔截器: AfterReturningAdviceInterceptor
  • @AfterThrowing
    • Advice 和攔截器: AspectJAfterThrowingAdvice
  • ThrowsAdviceAdapter 適配器,沒有對應的註解
    • Advice: ThrowsAdvice
    • 攔截器: ThrowsAdviceInterceptor

「攔截器鏈」執行過程的時序圖#

Mermaid Loading...

Refs:#

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。