先前幾篇都是概念的講解:回顧下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.
ApplicationContext委託PostProcessorRegistrationDelegate工具類執行post processors.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()方法執行全部BeanFactoryPostProcessor
invokeBeanFactoryPostProcessors:大致分爲兩步
- 首先執行BeanDefinitionRegistryPostProcessor. 調用其postProcessBeanDefinitionRegistry註冊BeanDefinition
- 而後執行BeanFactoryPostProcessor 調用postProcessBeanFactory 提供修改BeanDefinition功能.
ConfigurationClassPostProcessor做爲一個BeanDefinitionRegistryPostProcessor. 首先被執行.其processConfigBeanDefinitions方法:是收集BeanDefinition的開始的地方.
ConfigurationClassPostProcessor收集步驟:
- 從倉庫中找出ConfigurationClass, 由於是初期, 因此庫中只有springboot項目的啓動文件這個ConfigurationClass. 此ConfigurationClass也能夠看作是頂級ConfigurationClass ,就類型頂級xml文件, 去關聯其餘xml同樣. 啓動文件ConfigurationClass 做爲一個入口存在.
- 建立一個ConfigurationClassParser準備解析ConfigurationClass.
- 調用
**ConfigurationClassParser.parse()**
解析剛找到的ConfigurationClass. 經過此入口.把全部須要ConfigurationClass找到. 就相似.經過一個xml文件把其關聯的全部xml找到同樣. 類比理解.
- 建立一個
**ConfigurationClassBeanDefinitionReader.loadBeanDefinitions() **
從找到的全部ConfigurationClass中. 遍歷讀取每一個ConfigurationClass中的Bean定義.
分支解析:
第三步ConfigurationClassParser.parse()分支: 目的找到全部須要的ConfigurationClass.(理解爲找到xml文件)
此分支又大致分爲2部分:
- parse():processConfigurationClass()-->doProcessConfigurationClass():
- processDeferredImportSelectors(): 處理須要延遲處理的ImportSelector . 這裏的延遲加載最終仍是會走processImports()邏輯.
由於第二步最終仍是走第一步,因此只研究第一步。
doProcessConfigurationClass : 解析ConfigurationClass:(解析的過程涉及到不少遞歸. 經過一個ArrayDeque雙端棧來存儲當前的ConfigurationClass)
- processMemberClasses 遞歸處理內部類. 檢查當前ConfigurationClas的內部類是不是一個ConfigurationClass. 若是是遞歸processConfigurationClass()-->doProcessConfigurationClass().
- 處理@PropertySource註解
- 處理 @ComponentScan註解: 這裏也有一個遞歸操做. 檢查掃描的定義集以獲取任何進一步的配置類,並在須要時遞歸解析.這會用ComponentScanAnnotationParser解析器找到定義集中全部被@Component標註的類. 把其也當作一個ConfigurationClass. 而後遞歸解析ConfigurationClass
- processImports() 遞歸解析@Import註解引入的配置類. @Import導入的類會分紅三種狀況來處理.【具體看下面processImports()解析】
- 處理@ImportResource 註解. 把引入的資源值添加當前ConfigurationClass的importedResources屬性上.
- 處理@Bean:查看當前ConfigurationClass是否有@Bean的方法. 有就添加到ConfigurationClass的beanMethods屬性上.
- 若是父類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), 而後入庫到倉庫中的過程。
歡迎你們關注個人公衆號【源碼行動】,最新我的理解及時奉送。