細細拆分了一下Spring容器的refresh過程,真難...

細細拆分了一下Spring容器的refresh過程,真難...


細細拆分了一下Spring容器的refresh過程,真難...


我們分析了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方法:

@Overrideprotected 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的實現:

@Overrideprotected 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>類型的configurationClasses屬性用於保存解析的類,ConfigurationClass是一個對要解析的配置類的封裝,內部存儲了配置類的註解信息、被@Bean註解修飾的方法、@ImportResource註解修飾的信息、ImportBeanDefinitionRegistrar等都存儲在這個封裝類中。/<configurationclass>

這裡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); }}/<configurationclass>


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容器:


Tomca

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

所謂技多不壓身,我們所讀過的每一本書,所學過的每一門語言,在未來指不定都能給我們意想不到的回饋呢。其實做為一個開發者,有一個學習的氛圍跟一個交流圈子特別重要這裡我推薦一個Java學習交流群私信進群資料小編獲得,不管你是小白還是大牛歡迎入駐,大家一起交流成長。



分享到:


相關文章: