簡述 Spring AOP 的設計理念和源碼實現

前言

不管是採用 XML 仍是註解方式,最終 Spring 讀取加載後都會生成與之對應的 BeanDefinition,而後利用它就能夠去實例化一個對象。java

BeanDefinition 用來描述建立一個實例所須要的信息express

簡單的看來建立一個對象其實就 2 步驟緩存

  1. 讀取類,而後根據註解或者 XML 配置文件,將其封裝成 BeanDefinition
  2. 根據 BeanDefinition 包含的信息來實例化一個對象

Spring 就在這幾個步驟中穿插邏輯,從而拓展出了一個生態,下面咱們就來看看 AOP 的實現原理app

示例代碼

基本用法這裏就再也不贅述框架

裝載 BeanDefinition

調用 BeanFactoryProcessor

new AnnotationConfigApplicationContext(Class<?>... componentClasses) 首先將 AppConfig 解析爲 BeanDefinition 放入 BeanFactoryBeanDefinitionMappost

而後調用 refresh() 方法中,會調用 invokeBeanFactoryPostProcessors(beanFactory); 在這個方法中去調用全部的 BeanFactoryProcessorspa

那麼什麼是 BeanFactoryProcessor 它又有什麼做用呢?看下面這段代碼代理

經過實現了 BeanFactoryProcessor 咱們能夠拿到 ConfigurableListableBeanFactory,經過容器咱們就能拿到 BeanDefinition 進行分析和修改,從而間接的影響 Bean 的實例化作到咱們無感知,無侵入的加強和拓展類的功能code

調用 BeanDefinitionRegistryPostProcessor

而後該方法中,會繼續調用 invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);(還調用了其它的方法,本文只討論這個方法),BeanDefinitionRegistryPostProcessor 它的做用是能夠像容器中添加新的 BeanDefinition,在這一步將解析 JavaConfig 對應的 BeanDefinition,而後解析 @ComponentScan(basePackages = "com.example.ana") 這個註解,將這個包下面的全部類,裝載爲 BeanDefinition 而後放入 BeanFactory 容器中component

到此爲止,包下面的全部類就已經掃描裝載完畢在 BeanFactoryBeanDefinitionMap

實例化

找到全部的 @Aspect

要想應用 advice 邏輯,首先咱們須要找到對應的切面,而後咱們在其中查看須要在哪些方法中織入對應的 advice 邏輯,好比在 UserServiceImplqueryUser 這個 JoinPoint 切點以後織入 afterQuery

這分爲 2 步,先來看是如何查找切面 @Aspect 的,從 refresh() 中看起走,到這個位置

在 createBean 中調用了 resolveBeforeInstantiation 這個方法,目的就是給 BeanPostProcessor 一個返回代理對象的機會,好比下圖將全部 beanName 名字爲 a 的對象替換成對象 C

它有它的做用好比

  1. 制特定目標bean的默認實例化
  2. 執行值的注入等

咱們這裏是爲了尋找對應的 @Aspect 接着往下看,獲取全部的 BeanPostProcessor 須要注意的是除去咱們本身定義的 BeanPostProcessor 以外還有框架自身的一些實現類,而後若是發現類是 InstantiationAwareBeanPostProcessor 的話

  1. 取出 BeanFactory 中的全部的 bean 的類型
  2. 經過反射判斷對應的類型是否實現了 @Aspect 這個註解
  3. 而後將註解類對應的 advice 的相關信息緩存到 advisorsCache

首次調用 BeanPostProcessor 的實現就會作以上這件事,後續的調用就直接從緩存中取用便可

檢測對象是否須要被代理

如今全部的 @Aspect 都已經緩存好了,如今就要知道如何在調動業務邏輯的時候,如何織入對應的 advice 邏輯,好比在調用一個事務方法的時候,系統是怎麼幫我作到,自動打開事務,方法結束的時候提交事務的,這就須要一個代理對象幫我去作這件事情,那麼何時該建立代理對象,代理對象又是如何執行對應的 advice 方法呢

舉個很簡單的例子

User user = new User("小李");
user.getName();
複製代碼

這裏就只有一個獲取名稱的方法,其它什麼都不須要,那你說還須要爲它建立代理對象嗎,確定不須要,再看下面這個

@Transactional
public void saveXXX() {
    xxxDao.saveUser();
    xxxDao.saveProject();
}
複製代碼

這個方法咱們添加了事務,要求只要其中一個失敗就得進行回滾從而保證這個操做是一個原子操做,這個方法確定就須要建立代理對象了

就這個例子來看,當咱們發現所調用方法的註解具備必定的邏輯功能的話就須要建立代理對象,拓展來看就是說,咱們須要判斷一個類,或者類中是否存在某個方法須要被加強處理,從而來決定是否建立代理對象

那麼在咱們這個例子中,在實例化每一個對象的時候,就是首先從 advisorsCache 取出 advisor 檢測當前類是否知足 pointcutexpression 條件,知足的話就須要爲其建立代理對象,而且將匹配到的 advisor 存入代理對象中,最後容器中最後的實現類也變成了對應的代理類

建立代理對象

1) 早期 bean 對象的建立

resolveBeforeInstantiation 方法後就會調用 doCreateBean 來建立實例,在這裏面首先會建立一個早期 bean 對象,這個對象主要是用於解決循環依賴問題,好比

class A {
    B b;
}
class B {
    A a;
}
複製代碼

他們都是單例,Spring 在實例化 A 的時候,發現依賴了 B 須要去實例化 B,在實例化 B 的時候又發現須要實例化 A,形成循環依賴沒法實例化成功

Spring 解決這個問題的核心思想很是簡單,就是先建立一個早期 A 對象,僅僅是一個沒有填充屬性的引用,而後去建立 B 對象的時候再填充 A 就成功了,方法遞歸返回 A 對象也就建立成功了,容器已經持有了對應的引用,那麼在後續填充完各自的屬性後就算建立完整了

2)填充 Bean 的相關屬性

上一步操做獲得了一個早期的 bean 對象,而且放入了早期容器中下一步就是調用 populateBean 對 bean 的屬性就行填充,主要作如下 2 件事情

  • 注入依賴對象(處理 @Autowired )
    • 若是依賴的對象未被建立,則會進行遞歸建立
  • 處理 @Value 將配置文件中的值注入到實例中去

3)初始化 Bean

若是類實現了 InitializingBean 接口,就調用它的 afterPropertiesSet 進行值的設置

若是 XML 定義了 init-method 的話後續就會調用 invokeCustomInitMethod,而後調用對應的初始化方法進行初始化

4)建立代理實例

在初始化 Bean 完成後就會調用 applyBeanPostProcessorsAfterInitialization,旨在初始化完成後再給與一次修改 Bean 的機會

在這個方法中會去獲取全部的 BeanPostProcessor 而後依次調用其 postProcessAfterInitialization 方法

除去咱們本身實現了 BeanPostProcessor 以外,框架自己還提供了不少用於自身使用,這些 Processor 會在 refresh() 中的 registerBeanPostProcessors 方法中進行註冊和排序

其中有一個是 AbstractAutoProxyCreator 的實現,調用它的 postProcessAfterInitialization 裏面會調用 wrapIfNecessary,這個方法首先它會去找到全部候選的 advisor,而後判斷當前類或者實例中的方法,是否知足 @PointCut 的表達式,將成功匹配到的 advisor 返回

最後就是建立代理對象,同時將找到的 advisor 設置進去

到此爲止一個完整的代理對象就建立完畢

經過代理對象執行 advice 邏輯

  1. 首先咱們以前設置好的 advice 方法做爲一個調用鏈 chain
  2. 根據這些參數建立 CglibMethodInvocation 而後調用 proceed
  3. 隨後就會按照已經排好的順序執行對應的 advice 方法和執行被代理的目標方法

總結

總的來講就分爲 2 步驟

  • 建立代理對象
    • 根據類和註解或者 XML 建立 BeanDefinition
    • 根據 BeanDefinition 找到全部的 advice 方法
    • 建立 Bean 對象
    • 判斷對象是否須要被代理(是否知足 @PointCut)中的表達式條件
      • 知足的話就建立代理對象而且將 advice 等信息進行緩存
      • 不知足的話就跳過
  • 調用 advice 邏輯和目標方法
相關文章
相關標籤/搜索