生產中有很多形式的的配置方式,本文僅分析注解配置。對於其他形式的配置區別主觀以為主要在配置文件的解析過程不同,不一一分析了。本文以利用Dubbo框架開發rpc服務端為例詳細闡述配置類的解析、數據保存、實例化以及注入到容器中。
通常,涉及到配置參數的框架類,基本都離不開配置的解析及配置的保存;至於配置的具體使用,各框架就自行處理了。對於Spring container而言,配置參數的具體使用就是在實例化Bean的時候使用。所以本文主要分兩大部分:1、配置類的解析及數據保存;2、Bean的實例化並注入。
具體示例配置如下:
跟蹤調試Spring的工作流程:
查看這個類的屬性:
各屬性的屬性如下:
構造函數首先調用父類GenericApplicationContext構造函數初始化BeanFactory,這個類中的屬性beanDefinitionMap保存bean初始化需要的相關信息。
其次本類中初始化reader和scanner, 分別用於記錄BeanDefinition和掃描可能的Bean。初始化之后reader和scanner后,其內部屬性如下:
一、配置類的解析及數據保存(register)
通過源碼跟蹤分析,此過程的最終目的就是將通用的注解配置以<key,value>的形式保存在DefaultListableBeanFactory的beanDefinitionMap中,使所有的bean全部暴露以便后續使用。該暴露非依賴循環,僅最外層可識別的所有bean。
二、Bean的實例化並注入(refresh)
beanDefinitionMap中有很多的bean定義,本文僅關注自定義的配置類providerConfiguration的實例化注入。在模板方法設計模式的文章中,以該方法闡述了實例化注冊的大致執行流程。
refresh方法中並沒有傳入beanFactory的參數,那么就得在此方法中獲得beanFactory,具體子流程如下:
進一步跟蹤調試,prepareBeanFactory方法中對beanFactory相關屬性進行框架本身的初始賦值(即與具體業務無關)。其中關於beanPostProcessor的如下:
進入方法invokeBeanFactoryPostProcessors(beanFactory)在context中調用Bean工廠后置處理器依賴處理beanDefinitionMap中的bean定義,將其依賴的bean注入到beanDefinitionMap中。核心方法為processConfigBeanDefinitions(registry),通過該方法即可知其為處理配置bean定義。其處理核心邏輯為:
1、將所有的配置類列出
2、配置解析對象ConfigurationClassParser解析候選配置類保存至ConfigurationClass中:按層次遞歸處理配置類及其子類
與示例配置類相關的解析注解有@PropertySource、@Import(@EnableDubbo中包含)、@Bean。屬性相關的解析不做詳細說明,主要闡述@Import/@Bean的解析。
3、@Import與@Bean的處理
在@Enable*注解的工作原理 - 池塘里洗澡的鴨子 - 博客園 (cnblogs.com)中,粗略涉及了@EnableDubbo。這里僅此分析@EnableDubbo中涉及動態注冊Bean(導入與ImportBeanDefinitionRegistrar相關)處理。在解析器中,通過configClass.addImportBeanDefinitionRegistrar直接將ImportBeanDefinitionRegistrar作為配置類保存至ConfigClass中。
對於dubbo自定義的注解也是@Import動態注冊Bean的組合注解:
同理,@Bean標記的Bean方法應該保存在beanMethos中。至此,與業務邏輯相關的bean定義全部保存到了configClasses中。最后通過loadBeanDefinitionsForConfigurationClass全部保存至beanDefinitionMap中。具體時序圖如下:
接下來的處理才是真正的實例化處理:
4、bean實例化
非懶加載的實例化均在refresh#finishBeanFactoryInitialization中,跟蹤調試大致流程如下:
如果有代理實例化前利用beanPostProcessor將原有bean替換為目標對象返回,沒有則直接利用反射實例化對象。關鍵部分如下: