spring源碼分析6: ApplicationContext的初始化與BeanDefinition的蒐集入庫

先前幾篇都是概念的講解:回顧下java

  • BeanDefinition 是物料
  • Bean是成品
  • BeanFactory是倉庫,存儲物料與成品
  • ApplicationContext初始化蒐集物料入庫,觸發生產線,取出物料生產Bean

本文研究springboot環境下,ApplicationContext的初始化, 可能是處理註解形式的Bean.spring

重要組件

1.PostProcessorRegistrationDelegate: 代理執行post processors的工具類緩存

postProcessor分爲兩種:springboot

  • BeanFactoryPostProcessor: 發生在BeanDefinition蒐集入庫階段
  • BeanPostProcessor: 發生在BeanDefintion生成Bean階段

BeanFactoryPostProcessor細分又分爲兩種:工具

  • BeanDefinitionRegistryPostProcessor:繼承BeanFactoryPostProcessor接口。其postProcessBeanDefinitionRegistry接口方法,具備註冊更多Bean的語義。
  • BeanFactoryPostProcessor:偏向於修改的語義。

2.ConfigurationClass: 這個類要理解, 表示的是配置類,是對一個具備配置語義的BeanDefinition的封裝. 怎麼理解這個呢. 我認爲能夠理解爲一個xml文件. 記得一開始使用spring的時候, 須要在xml中配置bean.
post

ConfigurationClass語義:代理

  • 一個ConfigurationClass 能夠看作一個xml文件. 表明註解: @Configuration
  • 一個ConfigurationClass 中能夠定義Bean. 相似一個xml中能夠定義Bean信息. 表明註解@Bean
  • 一個ConfigurationClass 能夠引入其餘ConfigurationClass, 相似xml文件中會引入其餘xml文件. 表明註解:@Import,@ComponentScan等

那麼,怎麼判斷他是一個ConfigurationClass呢?看下面code

3.ConfigurationClassUtils: ConfigurationClass工具類cdn

checkConfigurationClassCandidate()方法: 先取出BeanDefinition的註解信息.xml

  • 判斷是不是full ConfigurationClass . 判斷依據是是否被@Configuration註解標識,若是是必定是ConfigurationClass
  • 判斷是不是isLiteConfigurationCandidate, 判斷依據是否被@Component,@ComponentScan,@Import,@ImportResource註解標識.
    或者有@Bean標識的方法

總的來講是看其是否能夠輸出Bean。判斷其是否屬於一個ConfigurationClass

4.ConfigurationClassPostProcessor:屬於一種post processor. 實現了BeanDefinitionRegistryPostProcessor,間接實現了BeanFactoryPostProcessor. 因此具備 註冊,與修改的雙重功能.主要工做就是處理ConfigurationClass

5.ConfigurationClassParser: ConfigurationClass解析器.可以解析出項目中的ConfigurationClass. (類比能找到項目中全部的配置了Bean的xml)

6.ConfigurationClassBeanDefinitionReader: BeanDefintion讀取器, 從ConfigurationClass中讀取裏面的Bean定義(類比可以從XML中讀出Bean)

蒐集入庫:

建議配合源代碼閱讀

BeanDefintion的蒐集發生在refresh()初始化方法中invokeBeanFactoryPostProcessors(beanFactory)階段,執行BeanFactoryPostProcessor.

file

ApplicationContext委託PostProcessorRegistrationDelegate工具類執行post processors.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()方法執行全部BeanFactoryPostProcessor

file

invokeBeanFactoryPostProcessors:大致分爲兩步

  • 首先執行BeanDefinitionRegistryPostProcessor. 調用其postProcessBeanDefinitionRegistry註冊BeanDefinition
  • 而後執行BeanFactoryPostProcessor 調用postProcessBeanFactory 提供修改BeanDefinition功能.

ConfigurationClassPostProcessor做爲一個BeanDefinitionRegistryPostProcessor. 首先被執行.其processConfigBeanDefinitions方法:是收集BeanDefinition的開始的地方.

ConfigurationClassPostProcessor收集步驟:

  1. 從倉庫中找出ConfigurationClass, 由於是初期, 因此庫中只有springboot項目的啓動文件這個ConfigurationClass. 此ConfigurationClass也能夠看作是頂級ConfigurationClass ,就類型頂級xml文件, 去關聯其餘xml同樣. 啓動文件ConfigurationClass 做爲一個入口存在.
  2. 建立一個ConfigurationClassParser準備解析ConfigurationClass.
  3. 調用**ConfigurationClassParser.parse()**解析剛找到的ConfigurationClass. 經過此入口.把全部須要ConfigurationClass找到. 就相似.經過一個xml文件把其關聯的全部xml找到同樣. 類比理解.
  4. 建立一個**ConfigurationClassBeanDefinitionReader.loadBeanDefinitions() **從找到的全部ConfigurationClass中. 遍歷讀取每一個ConfigurationClass中的Bean定義.

分支解析:

第三步ConfigurationClassParser.parse()分支: 目的找到全部須要的ConfigurationClass.(理解爲找到xml文件)

此分支又大致分爲2部分:

  • parse():processConfigurationClass()-->doProcessConfigurationClass():
  • processDeferredImportSelectors(): 處理須要延遲處理的ImportSelector . 這裏的延遲加載最終仍是會走processImports()邏輯.

由於第二步最終仍是走第一步,因此只研究第一步。

doProcessConfigurationClass : 解析ConfigurationClass:(解析的過程涉及到不少遞歸. 經過一個ArrayDeque雙端棧來存儲當前的ConfigurationClass)

  1. processMemberClasses 遞歸處理內部類. 檢查當前ConfigurationClas的內部類是不是一個ConfigurationClass. 若是是遞歸processConfigurationClass()-->doProcessConfigurationClass().
  2. 處理@PropertySource註解
  3. 處理 @ComponentScan註解: 這裏也有一個遞歸操做. 檢查掃描的定義集以獲取任何進一步的配置類,並在須要時遞歸解析.這會用ComponentScanAnnotationParser解析器找到定義集中全部被@Component標註的類. 把其也當作一個ConfigurationClass. 而後遞歸解析ConfigurationClass
  4. processImports() 遞歸解析@Import註解引入的配置類. @Import導入的類會分紅三種狀況來處理.【具體看下面processImports()解析
  5. 處理@ImportResource 註解. 把引入的資源值添加當前ConfigurationClass的importedResources屬性上.
  6. 處理@Bean:查看當前ConfigurationClass是否有@Bean的方法. 有就添加到ConfigurationClass的beanMethods屬性上.
  7. 若是父類superclass存在,而且不是java包中的類,而且還沒有處理處理則返回它以便外層循環繼續. 這也是爲啥doProcessConfigurationClass會嵌套在一個do while的緣由.

processImports():@Import導入的類會分紅三種狀況來處理.

(1)導入的是一個實現了ImportSelector接口的類.

  • 若是實現DeferredImportSelector則做爲延遲處理對待.放到deferredImportSelectors 緩存中在processDeferredImportSelectors()分支中處理
  • 若是實現ImportSelector,執行selectImports方法遞歸處理可能被@Import註解的ConfigurationClass

(2)導入的是一個實現了ImportBeanDefinitionRegistrar接口的類:

  • 代表有須要手動註冊Bean到容器中操做. 把實現類添加到當前ConfigurationClass的importBeanDefinitionRegistrars屬性中.

(3)普通類,則把他當作一個ConfigurationClass處理走遞歸解析.

  • 當作一個ConfigurationClass遞歸處理.

小結:通過一些系列遞歸,解析後.最終的結果:就是蒐集到ConfigurationClass . ConfigurationClass 裏攜帶這各類各樣的Bean定義.

今後處咱們也能夠看出一些東西若是向容器中註冊組件

  • 註解@Controller/@Service/@Repository/@Component
  • @Bean 返回的Bean .
  • @Import 快速導入一個組件到容器中 :普通類或者 ImportSelector接口實現類;或者ImportBeanDefinitionRegistrar實現類
  • @ImportResource 導入一個xml文件。

ConfigurationClassBeanDefinitionReader.loadBeanDefinitions()分支: 目的把ConfigurationClass中配置的BeanDefinition解析出來.(理解爲解析xml文件中的Bean定義)

有了ConfigurationClass,下面就是解析. 就比如有了xml文件,就能夠解析xml中的Bean定義同樣.這部分講講ConfigurationClass中的BeanDefinition如何被解析出來.

循環遍歷全部的找到的ConfigurationClass

  • configClass.isImported():當前ConfigurationClass是不是經過別人經過@Import引入的,是,當作一個BeanDefinition注入到倉庫中。
  • configClass.getBeanMethods(): 將@Bean註解標緻的方法的返回值解析成一個BeanDefinition注入到倉庫中。
  • loadBeanDefinitionsFromImportedResources(configClass.getImportedResources()):加載當前ConfigurationClass中@ImportResource引入的xml文件中的Bean定義。具體是使用XmlBeanDefinitionReader讀取器讀取XML中配置的Bean定義, 解析成BeanDefinition注入到倉庫中。
  • loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars()):取出實現了ImportBeanDefinitionRegistrar接口的類,執行其registerBeanDefinitions方法。註冊相應的BeanDefinition到倉庫中。

小結:

將ConfigurationClass中的BeanDefinition解析出來放入到倉庫中即完成了 BeanDefinition的蒐集工做。

回看上面的若是向容器中註冊組件除去註解@Controller/@Service/@Repository/@Component標緻的類被解析ConfigurationClass自己也是一種BeanDefinition外。

loadBeanDefinitions()解析的就是針對後三種方式的註冊Bean方式:

  • @Bean 標註
  • @Import 引入的三種。
  • @ImportResource 引入的xml文件。

總結:

總結來看:BeanDefinition的蒐集入庫階段,其實就是找Bean定義的配置文件(ConfigurationClass), 解析文件中Bean定義(BeaDefinition), 而後入庫到倉庫中的過程。

歡迎你們關注個人公衆號【源碼行動】,最新我的理解及時奉送。在這裏插入圖片描述

相關文章
相關標籤/搜索