在前一篇文章Spring探祕2:ApplicationContext啓動流程中提到了Spring容器啓動的最後一步是refresh,即配置的刷新,這是容器啓動過程中一個核心的步驟,實現了啓動容器的主要的功能。本文會簡單介紹一下AbstractApplicationContext#refresh()
方法的流程,其中涉及到的比較複雜的過程還需要另外詳細分析。首先其源碼如下:
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 讓容器準備好刷新。
prepareRefresh();
// 通知子類刷新其內部的Bean工廠
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 爲容器準備好Bean工廠
prepareBeanFactory(beanFactory);
try {
// 目前是一個空方法
postProcessBeanFactory(beanFactory);
// 調用容器中已註冊爲Bean的所有BeanFactoryPostProcessor
invokeBeanFactoryPostProcessors(beanFactory);
// 註冊所有的BeanPostProcessor(攔截Bean創建過程)
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
prepareRefresh()
- 讓容器準備好刷新,設置啓動時間和活動標識
- 初始化property源,驗證環境中的property是否合法(即判斷其可解析)
obtainFreshBeanFactory()
該方法是用於從子類中獲取實際的Bean工廠對象。
- 先調用refreshBeanFactory()通知子類刷新Bean工廠
- 在GenericApplicationContext#refreshBeanFactory()中用了一個CAS操作將容器的refreshed屬性置爲true
- 再調用抽象方法getBeanFactory()獲取Bean工廠,這個抽象方法就是留給子類實現的工廠方法
因此這裏就是一個工廠模式的實例
prepareBeanFactory()
該方法是在獲取到了容器中的Bean工廠後,配置其中的一些工具與屬性。
- 設置類加載器
- 設置Bean表達式解析器,bean初始化完成後填充屬性時會用到
- 添加一個PropertyEditorRegistrar,屬性編輯器註冊器,用於將屬性編輯器(PropertyEditor)註冊到容器中
- 添加一個BeanPostProcessor (ApplicationContextAwareProcessor),其功能是爲各種實現了Aware接口的bean注入其需要的容器中的信息
- 設置各種Aware實現類爲忽略自動裝配
- 設置自動裝配的類及自動裝配的對象
- 根據需要添加動態織入的功能
- 註冊各種默認的環境bean(Environment, SystemProperty, SystemEnvironment)
postProcessBeanFactory()
該方法在框架中是一個空方法,是預留給子類去實現的,用於在Bean工廠準備好後的一些處理工作。
invokeBeanFactoryPostProcessors()
該方法是刷新過程中最核心的一個方法,調用了容器中的BeanFactoryPostProcessor(即Bean工廠後置處理器),這是Spring框架向外暴露的一個擴展點,開發者可以通過實現該接口來對Bean工廠進行一些增強。這個方法的流程簡單來說是這樣:
- 先找到容器中所有的BeanDefinitionRegistryPostProcessor接口(它是BeanFactoryPostProcessor的子接口)的實現類,按順序調用其postProcessBeanDefinitionRegistry()方法
- 再調用這些BeanDefinitionRegistryPostProcessor的postProcessBeanFacory()方法
- 最後再調用普通的BeanFactoryPostProcessor的postProcessBeanFactory()方法
具體的流程還需要單獨再進行分析。
在默認情況下,此時容器中只有一個ConfigurationClassPostProcessor,它實現了BeanDefinitionRegistryPostProcessor接口。顧名思義該類的功能是在Bean工廠準備好後,處理容器中處理配置類。它的兩個主要功能如下:
- postProcessBeanDefinitionRegistry()方法:找到所有的配置類,並解析這些配置類(這裏所說的配置類是廣義上的配置類,只要有相關的配置註解的類都會被解析)。主要處理了以下的註解:
- @PropertySource
- @ComponentScan:掃描了該註解指定的包,並將包內所有@Component註解的類都生成BeanDefinition並註冊到容器中
- @Import: 根據Import註解傳入的類不同(ImportSelector, ImportBeanDefinitionRegistar, 其他)有不同的處理流程,處理結束後該註解引入的類的BeanDefinition應該已經註冊到容器中了
- @ImportResource
- 配置類中的@Bean方法
- 接口中的默認方法
- 如果配置類有父類,還要處理其父類
- postProcessBeanFactory()方法:對於有@Configuration註解的配置類的BeanDefinition,會將他們替換爲CGLib增強的子類。
這裏要爲什麼要做CGLib代理?看了一些源碼可知道,最根本的原因是爲了增強配置類中的@Bean方法,使得即使多次調用這些方法也能夠得到同一個Bean對象,實現Bean的單例模式。
ConfigurationClassPostProcessor是Spring框架中最重要的一個後置處理器,實現以上功能的原理以及具體的流程也需要單獨分析。
registerBeanPostProcessor()
會將所有實現了BeanPostProcessor接口的Bean註冊到容器中
initMessageSource()
在容器中初始化消息源
initApplicationEventMulticaster()
初始化事件廣播器
onRefresh()
空方法,也是爲子類預留的,令子類能夠通過重寫該方法來初始化一些特殊的Bean。
registerListeners()
將實現了ApplicationListener接口的Bean註冊到容器中作爲監聽器
finishBeanFactoryInitialization()
完成Bean工廠的初始化,生成所有單例Bean。即實例化Bean就是主要在該方法中進行的。
finishRefresh():
完成容器的刷新過程
- 初始化容器的生命週期處理器並通知其容器正在刷新
- 發佈容器刷新完成的事件