Spring原始碼之容器的功能拓展-ApplicationContext
- 一、解析預備 重新整理上下文環境 例如對系統屬性或者環境變數進行校驗和準備
- 二、初始化 BeanFactory 並進行 Xml 配置檔案的讀取
- 三、對BeanFactory 各種功能填充
- 四、啟用以及註冊各種 BeanFactoryPostProcessor 後處理器
- 五、註冊後處理器 BeanPostProcessor
- 後續環節
PS * 本文程式碼基本為虛擬碼,註釋為個人理解,水平有限,如有謬誤,感謝指正。
關於spring的容器,除了BeanFactory以及它的預設實現類XmlBeanFactory之外。
Spring還提供了 ApplicationContext ,
它用於對 BeanFactory的拓展。
本文入口:
ApplicationContext bf = new ClassPathXmlApplicationContext("bean.xml"");
核心程式碼:
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { prepareRefresh();//解析預備 重新整理上下文環境 例如對系統屬性或者環境變數進行校驗和準備 // 初始化 beanFactory 並讀取xml 配置檔案 , 此函式過後即擁有了 BeanFactory的全部功能 // 向下轉型 實際持有 它的子類: DefaultListableBeanFactory 型別的物件 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // ****#### DefaultListableBeanFactory <- beanFactory // 功能拓展 // beanFactory 初始化 <功能填充> 複用 BeanFactory 中的 配置檔案讀取、解析 以及其他功能 prepareBeanFactory(beanFactory); try { postProcessBeanFactory(beanFactory);// 鉤子函式,由子類實現 後處理器 // 啟用各種 BeanFactory 處理器 // postProcessor 後處理器 // ** 啟用 ** 註冊的 各種 BeanFactoryPostProcessor // **啟用** 且 **註冊** invokeBeanFactoryPostProcessors(beanFactory); // ** 註冊 ** BeanPostProcessors ,用於攔截 Bean 的建立 // 《後處理器》,僅僅註冊,在getBean方法呼叫時才會實際觸發 《不啟用》 // 因為僅僅註冊,所以不需要考慮 硬編碼方式的後處理器 // 對於硬編碼方式的後處理器 僅僅在 getBean時被呼叫 registerBeanPostProcessors(beanFactory); // 註冊最終呼叫 AbstractBeanFactory.addBeanPostProcessor() // AbstractBeanFactory <== AbstractAutowireCapableBeanFactory <== DefaultListableBeanFactory // 為上下文初始化message 源 ,即不同語言的訊息體(國際化處理) initMessageSource(); // 初始化 應用訊息 廣播器,並放入 "applicationEventMulticaster" bean 中 initApplicationEventMulticaster(); // 留個子類來初始化其它的 bean 《鉤子函式》 onRefresh(); // 在所有註冊的bean中,查詢 Listener-bean , 註冊到訊息廣播器中 registerListeners(); // 初始化 非延遲載入單例 finishBeanFactoryInitialization(beanFactory); // 完成重新整理過程,通知生命週期處理器 lifecycleProcessor 重新整理過程 // 發出 ContextRefreshEvent 通知別人 finishRefresh(); } catch (BeansException ex) { destroyBeans(); cancelRefresh(ex); throw ex; } finally { resetCommonCaches(); } } }
一、解析預備 重新整理上下文環境 例如對系統屬性或者環境變數進行校驗和準備
- 定義鉤子函式:initPropertySources() 當需要校驗時由子類實現該方法<模板方法模式>
- 通過 getEnvironment().validateRequiredProperties() 校驗
protected void prepareRefresh() { // 子類實現該函式,並設定需要校驗的 內容 initPropertySources();// 鉤子函式 初始化上下文環境中的任何佔位符屬性資源 // 驗證需要的屬性檔案是否已經 放入環境中 《《《 initPropertySources 是否完成工作 ?? getEnvironment().validateRequiredProperties(); }
二、初始化 BeanFactory 並進行 Xml 配置檔案的讀取
-
已知的是ApplicationContext 是對BeanFactory的 拓展,經過這一步之後,ApplicationContext 將擁有 BeanFactory 的全部功能;
-
AbstractRefreshableApplicationContext 中 實現的方法 refreshBeanFactory() 會產生一個預設的 DefaultListableBeanFactory 物件,
-
並載入類的 xml 配置 ,最終 ApplicationContext 將持有該 DefaultListableBeanFactory 物件;因此說它擁有 BeanFactory 的所有功能。
@Override
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {// 已有,不再重複該邏輯
destroyBeans();
closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());// 為了序列化指定ID
// 定製 beanFactory 包括是否允許覆蓋同名稱不同定義的物件、迴圈依賴
// 設定 @AutoWired 和 @Qualifier 的註解解析器 QualifierAnnotationAutowireCandidateResolver
customizeBeanFactory(beanFactory);
// 初始化 DocumentReader 並進行 xml 讀取、解析
loadBeanDefinitions(beanFactory);// 類 AbstractXmlApplicationContext 提供實現
this.beanFactory = beanFactory;// 記錄到全域性變數
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
三、對BeanFactory 各種功能填充
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
-
定製 BeanFactory 設定 beanFactory 的類載入器 為當前的 context的 類載入器
-
增加對SpEL 語言的支援
-
登錄檔達式語言SpEL解析器/處理器, 它主要是在依賴解析注入bean的時候、完成bean的初始化和屬性獲取後的屬性填充的時候呼叫。
-
例如解析: #{bean.filed}
loadBean[ BeanFactory.getBean(beanName); ] 過程中的屬性注入注入環節:applyPropertyValues,
會呼叫方法 BeanDefinitionValueResolver.resolveValueIfNecessary() 解析上述定義的引數
- 增加 屬性註冊編輯器
- 新增預設的propertyEditor,它主要是:對bean的屬性等設定進行管理,的一個工具
例如在 XML 配置中定義
<property name="currentDate">
<value>2021-05-03</value>
</property>
<property name="name">
<value>張二狗</value>
</property>
在 bean 中定義:
private Date currentDate;
private String name;
當spring 進行注入的時候,可以把普通屬性name注入進來,但是卻不能識別bean中定義的Date型別currentDate;
有兩種解決方法:
a. 使用自定義屬性編輯器即可,需要繼承 類: Property|EditorSupport 並重寫 setAsText() 方法;
b. 使用spring 自帶的屬性編輯器:CustomDateEditor,
設定屬性註冊編輯器 AbstractBeanFactory.addPropertyEditorRegistrar();
- spring自帶屬性編輯器,使用案例詳見: AbstractBeanFactory.initBeanWrapper() -> registerCustomEditors();
-> ResourceEditorRegistrar.registerCustomEditor() 註冊常用的 屬性編輯器
- 增加 ApplicationContextAwareProcessor 處理器,該後處理器用於對 容器的補充/增強,而非普通bean的後處理器
在init-method的前後,將呼叫該處理器的postProcessBeforeInitialization()方法 和postProcessAfterInitialization()方法;
- 設定 忽略依賴
當 Spring 將ApplicationContextAwareProcessor 註冊後,在 invokeAwareInterfaces() 方法中 間接呼叫的Aware 類已經不是普通的 bean 了,
如:ResourceLoaderAware,所以需要在spring進行bean的依賴注入的時候忽略掉它們。
- 註冊依賴
同樣spring 也提供了 註冊依賴的功能
// 設定幾個忽略自動裝配的特殊規則
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
當解析到對型別 BeanFactory.class 的依賴時,會直接將它的例項 beanFactory 注入。
四、啟用以及註冊各種 BeanFactoryPostProcessor 後處理器
PostProcessorRegistrationDelegate
.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
-
後處理器的作用範圍是容器級的,僅僅對當前容器中的 bean 生效;
-
需要啟用 以及 註冊(提取並呼叫)
-
還需要考慮 各種後處理器是否 實現了排序介面
-
BeanFactoryPostProcessor 可以對 bean 定義的元資料進行處理,
可以說Spring 容器 允許 BeanFactoryPostProcessor , 在實際例項化任意bean之前讀取配置的元資料並進行處理。 -
可以分為xml 檔案配置的 BeanFactoryPostProcessor 後處理器 和 硬編碼型別的 BeanFactoryPostProcessor 後處理器。
五、註冊後處理器 BeanPostProcessor
-
僅僅對 BeanPostProcessor 型別的後處理器進行註冊,實際呼叫發生在 getBean(beanName); 例項化bean的時候
-
類似對 BeanFactoryPostProcessor 的處理,區別是 BeanPostProcessor 由於只需要註冊不需要啟用,所以它只處理配置檔案中配置的 BeanPostProcessor ,而不處理硬編碼型別的 BeanPostProcessor
後續環節
後續環節還包括:
-
為上下文初始化message 源 ,即不同語言的訊息體(國際化處理)
-
初始化 應用訊息 廣播器,並放入 "applicationEventMulticaster" bean 中
-
在所有註冊的bean中,查詢 Listener-bean , 註冊到訊息廣播器中
-
初始化 非延遲載入單例
-
完成重新整理過程,通知生命週期處理器 lifecycleProcessor 重新整理過程
。。。。 等等
由於邏輯並不複雜,不再記錄。
-- 本文僅為個人學習spring原始碼後的理解。