上一篇文章中,我們分析了SpringBoot的啟動過程:構造SpringApplication并調用它的run方法。其中構造SpringApplication的時候會初始化一些監聽器和初始化器;run方法調用的過程中會有對應的監聽器監聽,并且會創建Spring容器。
Spring容器創建之后,會調用它的refresh方法,refresh的時候會做很多事情:比如完成配置類的解析、各種BeanFactoryPostProcessor和BeanPostProcessor的注冊、國際化配置的初始化、web內置容器的構造等等。
我們來分析一下這個refresh過程。
還是以web程序為例,那么對應的Spring容器為AnnotationConfigEmbeddedWebApplicationContext。它的refresh方法調用了父類AbstractApplicationContext的refresh方法:
public void refresh() throws BeansException, IllegalStateException {
// refresh過程只能一個線程處理,不允許并發執行
synchronized (this.startupShutdownMonitor) {
prepareRefresh();
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
prepareBeanFactory(beanFactory);
try {
postProcessBeanFactory(beanFactory);
invokeBeanFactoryPostProcessors(beanFactory);
registerBeanPostProcessors(beanFactory);
initMessageSource();
initApplicationEventMulticaster();
onRefresh();
registerListeners();
finishBeanFactoryInitialization(beanFactory);
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
destroyBeans();
cancelRefresh(ex);
throw ex;
}
finally {
resetCommonCaches();
}
}
}
prepareRefresh方法
表示在真正做refresh操作之前需要準備做的事情:
- 設置Spring容器的啟動時間,撤銷關閉狀態,開啟活躍狀態。
- 初始化屬性源信息(Property)
- 驗證環境信息里一些必須存在的屬性
prepareBeanFactory方法
從Spring容器獲取BeanFactory(Spring Bean容器)并進行相關的設置為后續的使用做準備:
- 設置classloader(用于加載bean),設置表達式解析器(解析bean定義中的一些表達式),添加屬性編輯注冊器(注冊屬性編輯器)
- 添加ApplicationContextAwareProcessor這個BeanPostProcessor。取消ResourceLoaderAware、ApplicationEventPublisherAware、MessageSourceAware、ApplicationContextAware、EnvironmentAware這5個接口的自動注入。因為ApplicationContextAwareProcessor把這5個接口的實現工作做了
- 設置特殊的類型對應的bean。BeanFactory對應剛剛獲取的BeanFactory;ResourceLoader、ApplicationEventPublisher、ApplicationContext這3個接口對應的bean都設置為當前的Spring容器
- 注入一些其它信息的bean,比如environment、systemProperties等
postProcessBeanFactory方法
BeanFactory設置之后再進行后續的一些BeanFactory操作。
不同的Spring容器做不同的操作。比如GenericWebApplicationContext容器會在BeanFactory中添加ServletContextAwareProcessor用于處理ServletContextAware類型的bean初始化的時候調用setServletContext或者setServletConfig方法(跟ApplicationContextAwareProcessor原理一樣)。
AnnotationConfigEmbeddedWebApplicationContext對應的postProcessBeanFactory方法:
@Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// 調用父類EmbeddedWebApplicationContext的實現
super.postProcessBeanFactory(beanFactory);
// 查看basePackages屬性,如果設置了會使用ClassPathBeanDefinitionScanner去掃描basePackages包下的bean并注冊
if (this.basePackages != null && this.basePackages.length > 0) {
this.scanner.scan(this.basePackages);
}
// 查看annotatedClasses屬性,如果設置了會使用AnnotatedBeanDefinitionReader去注冊這些bean
if (this.annotatedClasses != null && this.annotatedClasses.length > 0) {
this.reader.register(this.annotatedClasses);
}
}
父類EmbeddedWebApplicationContext的實現:
@Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
beanFactory.addBeanPostProcessor(
new WebApplicationContextServletContextAwareProcessor(this));
beanFactory.ignoreDependencyInterface(ServletContextAware.class);
}
invokeBeanFactoryPostProcessors方法
在Spring容器中找出實現了BeanFactoryPostProcessor接口的processor并執行。Spring容器會委托給PostProcessorRegistrationDelegate的invokeBeanFactoryPostProcessors方法執行。
介紹兩個接口:
- BeanFactoryPostProcessor:用來修改Spring容器中已經存在的bean的定義,使用ConfigurableListableBeanFactory對bean進行處理
- BeanDefinitionRegistryPostProcessor:繼承BeanFactoryPostProcessor,作用跟BeanFactoryPostProcessor一樣,只不過是使用BeanDefinitionRegistry對bean進行處理
基于web程序的Spring容器AnnotationConfigEmbeddedWebApplicationContext構造的時候,會初始化內部屬性AnnotatedBeanDefinitionReader reader,這個reader構造的時候會在BeanFactory中注冊一些post processor,包括BeanPostProcessor和BeanFactoryPostProcessor(比如ConfigurationClassPostProcessor、AutowiredAnnotationBeanPostProcessor):
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
invokeBeanFactoryPostProcessors方法處理BeanFactoryPostProcessor的邏輯如下:
從Spring容器中找出BeanDefinitionRegistryPostProcessor類型的bean(這些processor是在容器剛創建的時候通過構造AnnotatedBeanDefinitionReader的時候注冊到容器中的),然后按照優先級分別執行,優先級的邏輯如下:
- 實現PriorityOrdered接口的BeanDefinitionRegistryPostProcessor先全部找出來,然后排序后依次執行
- 實現Ordered接口的BeanDefinitionRegistryPostProcessor找出來,然后排序后依次執行
- 沒有實現PriorityOrdered和Ordered接口的BeanDefinitionRegistryPostProcessor找出來執行并依次執行
接下來從Spring容器內查找BeanFactoryPostProcessor接口的實現類,然后執行(如果processor已經執行過,則忽略),這里的查找規則跟上面查找BeanDefinitionRegistryPostProcessor一樣,先找PriorityOrdered,然后是Ordered,最后是兩者都沒。
這里需要說明的是ConfigurationClassPostProcessor這個processor是優先級最高的被執行的processor(實現了PriorityOrdered接口)。這個ConfigurationClassPostProcessor會去BeanFactory中找出所有有@Configuration注解的bean,然后使用ConfigurationClassParser去解析這個類。ConfigurationClassParser內部有個Map<ConfigurationClass, ConfigurationClass>類型的configurationClasses屬性用于保存解析的類,ConfigurationClass是一個對要解析的配置類的封裝,內部存儲了配置類的注解信息、被@Bean注解修飾的方法、@ImportResource注解修飾的信息、ImportBeanDefinitionRegistrar等都存儲在這個封裝類中。
這里ConfigurationClassPostProcessor最先被處理還有另外一個原因是如果程序中有自定義的BeanFactoryPostProcessor,那么這個PostProcessor首先得通過ConfigurationClassPostProcessor被解析出來,然后才能被Spring容器找到并執行。(ConfigurationClassPostProcessor不先執行的話,這個Processor是不會被解析的,不會被解析的話也就不會執行了)。
在我們的程序中,只有主類RefreshContextApplication有@Configuration注解(@SpringBootApplication注解帶有@Configuration注解),所以這個配置類會被ConfigurationClassParser解析。解析過程如下:
- 處理@PropertySources注解:進行一些配置信息的解析
- 處理@ComponentScan注解:使用ComponentScanAnnotationParser掃描basePackage下的需要解析的類(@SpringBootApplication注解也包括了@ComponentScan注解,只不過basePackages是空的,空的話會去獲取當前@Configuration修飾的類所在的包),并注冊到BeanFactory中(這個時候bean并沒有進行實例化,而是進行了注冊。具體的實例化在finishBeanFactoryInitialization方法中執行)。對于掃描出來的類,遞歸解析
- 處理@Import注解:先遞歸找出所有的注解,然后再過濾出只有@Import注解的類,得到@Import注解的值。比如查找@SpringBootApplication注解的@Import注解數據的話,首先發現@SpringBootApplication不是一個@Import注解,然后遞歸調用修飾了@SpringBootApplication的注解,發現有個@EnableAutoConfiguration注解,再次遞歸發現被@Import(EnableAutoConfigurationImportSelector.class)修飾,還有@AutoConfigurationPackage注解修飾,再次遞歸@AutoConfigurationPackage注解,發現被@Import(AutoConfigurationPackages.Registrar.class)注解修飾,所以@SpringBootApplication注解對應的@Import注解有2個,分別是@Import(AutoConfigurationPackages.Registrar.class)和@Import(EnableAutoConfigurationImportSelector.class)。找出所有的@Import注解之后,開始處理邏輯:
- 遍歷這些@Import注解內部的屬性類集合
- 如果這個類是個ImportSelector接口的實現類,實例化這個ImportSelector,如果這個類也是DeferredImportSelector接口的實現類,那么加入ConfigurationClassParser的deferredImportSelectors屬性中讓第6步處理。否則調用ImportSelector的selectImports方法得到需要Import的類,然后對這些類遞歸做@Import注解的處理
- 如果這個類是ImportBeanDefinitionRegistrar接口的實現類,設置到配置類的importBeanDefinitionRegistrars屬性中
- 其它情況下把這個類入隊到ConfigurationClassParser的importStack(隊列)屬性中,然后把這個類當成是@Configuration注解修飾的類遞歸重頭開始解析這個類
- 處理@ImportResource注解:獲取@ImportResource注解的locations屬性,得到資源文件的地址信息。然后遍歷這些資源文件并把它們添加到配置類的importedResources屬性中
- 處理@Bean注解:獲取被@Bean注解修飾的方法,然后添加到配置類的beanMethods屬性中
- 處理DeferredImportSelector:處理第3步@Import注解產生的DeferredImportSelector,進行selectImports方法的調用找出需要import的類,然后再調用第3步相同的處理邏輯處理
這里@SpringBootApplication注解被@EnableAutoConfiguration修飾,@EnableAutoConfiguration注解被@Import(EnableAutoConfigurationImportSelector.class)修飾,所以在第3步會找出這個@Import修飾的類EnableAutoConfigurationImportSelector,這個類剛好實現了DeferredImportSelector接口,接著就會在第6步被執行。第6步selectImport得到的類就是自動化配置類。
EnableAutoConfigurationImportSelector的selectImport方法會在spring.factories文件中找出key為EnableAutoConfiguration對應的值,有81個,這81個就是所謂的自動化配置類(XXXAutoConfiguration)。
ConfigurationClassParser解析完成之后,被解析出來的類會放到configurationClasses屬性中。然后使用ConfigurationClassBeanDefinitionReader去解析這些類。
這個時候這些bean只是被加載到了Spring容器中。下面這段代碼是ConfigurationClassBeanDefinitionReader的解析bean過程:
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
for (ConfigurationClass configClass : configurationModel) {
// 對每一個配置類,調用loadBeanDefinitionsForConfigurationClass方法
loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
}
}
private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass,
TrackedConditionEvaluator trackedConditionEvaluator) {
// 使用條件注解判斷是否需要跳過這個配置類
if (trackedConditionEvaluator.shouldSkip(configClass)) {
// 跳過配置類的話在Spring容器中移除bean的注冊
String beanName = configClass.getBeanName();
if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
this.registry.removeBeanDefinition(beanName);
}
this.importRegistry.removeImportingClassFor(configClass.getMetadata().getClassName());
return;
}
if (configClass.isImported()) {
// 如果自身是被@Import注釋所import的,注冊自己
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
// 注冊方法中被@Bean注解修飾的bean
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
// 注冊@ImportResource注解注釋的資源文件中的bean
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
// 注冊@Import注解中的ImportBeanDefinitionRegistrar接口的registerBeanDefinitions
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
invokeBeanFactoryPostProcessors方法總結來說就是從Spring容器中找出BeanDefinitionRegistryPostProcessor和BeanFactoryPostProcessor接口的實現類并按照一定的規則順序進行執行。 其中ConfigurationClassPostProcessor這個BeanDefinitionRegistryPostProcessor優先級最高,它會對項目中的@Configuration注解修飾的類(@Component、@ComponentScan、@Import、@ImportResource修飾的類也會被處理)進行解析,解析完成之后把這些bean注冊到BeanFactory中。需要注意的是這個時候注冊進來的bean還沒有實例化。
registerBeanPostProcessors方法
從Spring容器中找出的BeanPostProcessor接口的bean,并設置到BeanFactory的屬性中。之后bean被實例化的時候會調用這個BeanPostProcessor。
該方法委托給了PostProcessorRegistrationDelegate類的registerBeanPostProcessors方法執行。這里的過程跟invokeBeanFactoryPostProcessors類似:
- 先找出實現了PriorityOrdered接口的BeanPostProcessor并排序后加到BeanFactory的BeanPostProcessor集合中
- 找出實現了Ordered接口的BeanPostProcessor并排序后加到BeanFactory的BeanPostProcessor集合中
- 沒有實現PriorityOrdered和Ordered接口的BeanPostProcessor加到BeanFactory的BeanPostProcessor集合中
這些已經存在的BeanPostProcessor在postProcessBeanFactory方法中已經說明,都是由AnnotationConfigUtils的registerAnnotationConfigProcessors方法注冊的。這些BeanPostProcessor包括有AutowiredAnnotationBeanPostProcessor(處理被@Autowired注解修飾的bean并注入)、RequiredAnnotationBeanPostProcessor(處理被@Required注解修飾的方法)、CommonAnnotationBeanPostProcessor(處理@PreDestroy、@PostConstruct、@Resource等多個注解的作用)等。
如果是自定義的BeanPostProcessor,已經被ConfigurationClassPostProcessor注冊到容器內。
這些BeanPostProcessor會在這個方法內被實例化(通過調用BeanFactory的getBean方法,如果沒有找到實例化的類,就會去實例化)。
initMessageSource方法
在Spring容器中初始化一些國際化相關的屬性。
initApplicationEventMulticaster方法
在Spring容器中初始化事件廣播器,事件廣播器用于事件的發布。
在SpringBoot源碼分析之SpringBoot的啟動過程中分析過,EventPublishingRunListener這個SpringApplicationRunListener會監聽事件,其中發生contextPrepared事件的時候EventPublishingRunListener會把事件廣播器注入到BeanFactory中。
所以initApplicationEventMulticaster不再需要再次注冊,只需要拿出BeanFactory中的事件廣播器然后設置到Spring容器的屬性中即可。如果沒有使用SpringBoot的話,Spring容器得需要自己初始化事件廣播器。
onRefresh方法
一個模板方法,不同的Spring容器做不同的事情。
比如web程序的容器AnnotationConfigEmbeddedWebApplicationContext中會調用createEmbeddedServletContainer方法去創建內置的Servlet容器。
目前SpringBoot只支持3種內置的Servlet容器:
- Tomcat
- Jetty
- Undertow
registerListeners方法
把Spring容器內的時間監聽器和BeanFactory中的時間監聽器都添加的事件廣播器中。
然后如果存在early event的話,廣播出去。
finishBeanFactoryInitialization方法
實例化BeanFactory中已經被注冊但是未實例化的所有實例(懶加載的不需要實例化)。
比如invokeBeanFactoryPostProcessors方法中根據各種注解解析出來的類,在這個時候都會被初始化。
實例化的過程各種BeanPostProcessor開始起作用。
finishRefresh方法
refresh做完之后需要做的其他事情。
- 初始化生命周期處理器,并設置到Spring容器中(LifecycleProcessor)
- 調用生命周期處理器的onRefresh方法,這個方法會找出Spring容器中實現了SmartLifecycle接口的類并進行start方法的調用
- 發布ContextRefreshedEvent事件告知對應的ApplicationListener進行響應的操作
- 調用LiveBeansView的registerApplicationContext方法:如果設置了JMX相關的屬性,則就調用該方法
- 發布EmbeddedServletContainerInitializedEvent事件告知對應的ApplicationListener進行響應的操作
總結
Spring容器的refresh過程就是上述11個方法的介紹。內容還是非常多的,本文也只是說了個大概,像bean的實例化過程沒有具體去分析,這方面的內容以后會看情況去做分析。
這篇文章也是為之后的文章比如內置Servlet容器的創建啟動、條件注解的使用等打下基礎。
例子
寫了一個例子用來驗證容器的refresh過程,包括bean解析,processor的使用、Lifecycle的使用等。
可以啟動項目debug去看看對應的過程,這樣對Spring容器會有一個更好的理解。
地址在:https://github.com/fangjian0423/springboot-analysis/tree/master/springboot-refresh-context