目錄
一、概觀Spring Boot
二、Spring Boot應(yīng)用初始化
2.1 初始化入口
2.2?SpringApplication的run方法
2.3 方法分析
三、容器創(chuàng)建與初始化
3.1 creatApplicationContext()方法
3.2 prepareContext(context, environment, listener, applicationArguments, printedBanner)方法
3.3 refreshContext(context)方法
四、總結(jié)
一、概觀SpringBoot
隨著分布式、微服務(wù)和云計(jì)算時(shí)代的到來(lái),Spring Boot框架的重要性日益突顯。Spring官方給出的Spring Boot框架的特性:
可以創(chuàng)建獨(dú)立的Spring應(yīng)用
直接內(nèi)嵌Tomcat, Jetty 或者 Undertow,不再需要開(kāi)發(fā)WAR類型文件
提供各式的"starter"依賴,來(lái)簡(jiǎn)化項(xiàng)目的創(chuàng)建配置
任何時(shí)候都盡可能地自動(dòng)化配置Spring和第三方庫(kù)
提供猶如健康指標(biāo)檢測(cè)和外部配置的生產(chǎn)環(huán)境特性
完全沒(méi)有代碼生成和不需要xml配置
從官方給出的特性來(lái)看,Spring Boot適合做分布式系統(tǒng)和云上應(yīng)用的底層框架,內(nèi)嵌容器不需要再安裝配置容器,而且提供了很多已經(jīng)配置好的第三方框架和應(yīng)用,并且主張“約定大于配置”,即拿來(lái)即用,適合使用java config注解配置,讓開(kāi)發(fā)人員更關(guān)注與業(yè)務(wù)代碼。所以,在這么多誘惑下,自己便決定研究一下Spring Boot框架的源碼,從源碼來(lái)了解Spring Boot的迷人之處。
二、Spring Boot應(yīng)用初始化
Spring框架的IoC和AOP是其強(qiáng)大之處,而Spring框架下的應(yīng)用都會(huì)使用這兩種技術(shù),所以使用該框架的開(kāi)發(fā)人員都應(yīng)該去了解ioc和aop技術(shù)。而Spring Boot是Spring框架的即用版,因此,從Spring Boot源碼中學(xué)習(xí)IoC容器的創(chuàng)建初始化以及AOP技術(shù)應(yīng)用的思想,是我認(rèn)為比較便捷的方法。同時(shí),還可以學(xué)習(xí)到Spring Boot在整合Spring和第三方框架及應(yīng)用的思想和技巧。接下來(lái),我將從Spring Boot應(yīng)用的啟動(dòng)入口開(kāi)始分析,逐步分析啟動(dòng)流程。
2.1 初始化入口
下面是Spring Boot應(yīng)用最簡(jiǎn)單的啟動(dòng)代碼:
@SpringBootApplication
public class MyApplication{
public static void main(String[] args){
SpringApplication.run(MyApplication.class, args );
}
}
從這段代碼可以知道,應(yīng)用的主方法main調(diào)用了SpringApplication.run()方法,開(kāi)啟了應(yīng)用初始化。那么,接下來(lái)我們看看它是怎么初始化容器的。
2.2 SpringApplication的run方法
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments,printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);}
try {
listeners.running(context);
}catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
2.3 方法分析
StopWatch類:計(jì)時(shí)類,計(jì)算SpringBoot應(yīng)用的啟動(dòng)時(shí)間。
SpringBootExceptionReporter類:是一個(gè)回調(diào)接口,用于支持SpringApplication啟動(dòng)錯(cuò)誤的自定義報(bào)告。
configureHeadlessProper()方法:配置Headless模式配置,該模式下系統(tǒng)缺少顯示設(shè)備、鼠標(biāo)或鍵盤(pán),而服務(wù)器端往往需要在該模式下工作。
getRunListeners(args)方法:獲取SpringApplicationRunListeners類,是SpringApplicationRunListener類的集合。
SpringApplicationRunListener類:SpringApplication的run方法的監(jiān)聽(tīng)器。
DefaultApplicationArguments(args)類:提供訪問(wèn)運(yùn)行一個(gè)SpringApplication的arguments的訪問(wèn)入口。
prepareEnvironment(listeners,applicationArguments)方法:創(chuàng)建和配置Environment,同時(shí)在調(diào)用AbstractEnvironment構(gòu)造函數(shù)時(shí)生成PropertySourcesPropertyResolver類。
configureIgnoreBeanInfo(environment)方法:配置系統(tǒng)IgnoreBeanInfo屬性。
printBanner(environment)方法:打印啟動(dòng)的圖形,返回Banner接口實(shí)現(xiàn)類。
createApplicationContext()方法:根據(jù)SpringApplication構(gòu)造方法生成的webApplicationType變量創(chuàng)建一個(gè)ApplicationContext,默認(rèn)生成AnnotationConfigApplicationContext。
getSpringFactoriesInstances(SpringBootExceptionRepoter.class, new Class[] {ConfigurableApplicationContext.class }, context)方法:獲取SpringBootExceptionReporter類的集合。
prepareContext(context, environment, listener, applicationArguments, printedBanner)方法:設(shè)置Environment,在ApplicationContext中應(yīng)用所有相關(guān)的后處理,在刷新之前將所有的ApplicationContextInitializers應(yīng)用于上下文,設(shè)置SpringApplicationRunLIstener接口實(shí)現(xiàn)類實(shí)現(xiàn)多路廣播Spring事件,添加引導(dǎo)特定的單例(SpringApplicationArguments, Banner),創(chuàng)建DefaultListableBeanFactory工廠類,從主類中定位資源并將資源中的bean加載進(jìn)入ApplicationContext中,向ApplicationContext中添加ApplicationListener接口實(shí)現(xiàn)類。
refreshContext(context)方法:調(diào)用AbstractApplicationContext的refresh()方法初始化DefaultListableBeanFactory工廠類。
afterRefresh(CongigurableApplicationContext context, ApplicationArguments args)方法:在刷新ApplicationContext之后調(diào)用,在SpringAppliation中是方法體為空的函數(shù),故不做任何操作。
listeners.started(context)方法:在ApplicationContext已經(jīng)刷新及啟動(dòng)后,但CommandLineRunners和ApplicationRunner還沒(méi)有啟動(dòng)時(shí),調(diào)用該方法向容器中發(fā)布SpringApplicationEvent類或者子類。
callRunners(context, applicationArguments)方法:調(diào)用應(yīng)用中ApplicationRunner和CommanLineRunner的實(shí)現(xiàn)類,執(zhí)行其run方法,調(diào)用時(shí)機(jī)是容器啟動(dòng)完成之后,可以用@Order注解來(lái)配置Runner的執(zhí)行順序,可以用來(lái)讀取配置文件或連接數(shù)據(jù)庫(kù)等操作。
listeners.running(context)方法:在容器刷新以及所有的Runner被調(diào)用之后,run方法完成執(zhí)行之前調(diào)用該方法。調(diào)用之前得到的SpringApplicationRunListeners類running(context)方法。
最后,向應(yīng)用中返回一個(gè)之前獲得到的ApplicationContext。
三、容器創(chuàng)建與初始化
在第二部分的SpringApplication的run()方法中,我們已經(jīng)大致了解到了Spring Boot應(yīng)用在啟動(dòng)時(shí)做了哪些工作,與容器初始化相關(guān)的方法是createApplciationContext()、prepareContext(context, environment, listener, applicationArguments, printedBanner)以及refreshContext(context)。那這一部分將關(guān)注與Spring IoC容器的創(chuàng)建和初始化相關(guān)的SpringApplication方法。
3.1 creatApplicationContext()方法
根據(jù)SpringApplication構(gòu)造方法生成的webApplicationType變量創(chuàng)建一個(gè)ApplicationContext,默認(rèn)生成AnnotationConfigApplicationContext。
(1)時(shí)序圖
(2)方法的源碼
protected ConfigurableApplicationContext createApplicationContext() {
Class contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}catch (ClassNotFoundException ex) {
throw new IllegalStateException("Unable create a default ApplicationContext, "
+ "please specify an ApplicationContextClass",ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
(3)方法分析
a. 方法根據(jù)applicationContextClass變量是否為空,決定是否執(zhí)行下面的switch語(yǔ)句,而applicationContextClass變量是由SpringApplicationBuilder類中的contextClass()方法調(diào)用ApplicationContext的setApplicationContextClass()賦值的,默認(rèn)為空;
b. webApplicationType變量是SpringApplication.run()方法調(diào)用構(gòu)造方法時(shí)賦值的;
c. switch語(yǔ)句通過(guò)反射根據(jù)webApplicationType生成對(duì)應(yīng)的容器,分別是AnnotationConfigServletWebServerApplicationContext、AnnotationConfigReactiveWebServerApplicationContext以及AnnotationConfigApplicationContext的,默認(rèn)生成的是AnnotationConfigApplicationContext,同時(shí),其構(gòu)造函數(shù)生成AnnotedBeanDefinitonReader和ClassPathBeanDefinitionScanner類;
d. 最后,通過(guò)BeanUtils工具類將獲取到的容器類轉(zhuǎn)換成ConfigurableApplicationContext類,返回給應(yīng)用使用。
3.2 prepareContext(context, environment, listener, applicationArguments, printedBanner)方法
prepareContext方法的作用是,設(shè)置Environment,在ApplicationContext中應(yīng)用相關(guān)的后處理,在刷新之前將任何的ApplicationContextInitializers應(yīng)用于上下文,設(shè)置SpringApplicationRunLIstener接口實(shí)現(xiàn)類實(shí)現(xiàn)多路廣播Spring事件,添加引導(dǎo)特定的單例(SpringApplicationArguments, Banner),創(chuàng)建DefaultListableBeanFactory工廠類,從主類中定位資源并將資源中的bean加載進(jìn)入ApplicationContext中,向ApplicationContext中添加ApplicationListener接口實(shí)現(xiàn)類。接下來(lái),我們將從源碼入手,逐步分析prepareContext()方法所做的工作。
(1)prepareContext()源碼
private void prepareContext(ConfigurableApplicationContext context,ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
Set sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context);
}
(2)prepareContext時(shí)序圖
(3)方法解析
context.setEnvironemnt(environment)方法:為容器設(shè)置環(huán)境,該Environment類是run()方法中的prepareEnvironment()方法獲取的,默認(rèn)為StandardEnvironment類。
postProcessApplicationContext(context)方法:在ApplicationContext中應(yīng)用相關(guān)的后處理。
a. 根據(jù)應(yīng)用中的beanNameGernerator引用是否為空(默認(rèn)為空),決定是否向容器中注入BeanNameGenerator類的實(shí)現(xiàn)類;
b. 根據(jù)resourceLoader引用是否為空(默認(rèn)為DefaultResourceLoader類,在printBanner()階段獲得)以及context的類型,決定配置resource的地方和方式,若context(默認(rèn)為AnnotationConfigApplicationContext)為GenericApplicationContext類或子類,則向context中添加resourceLoader,若context為DefaultResourceLoader類或子類,則向context中添加resourceLoader類的ClassLoader類實(shí)例;
c. 根據(jù)addConversionService的布爾值(默認(rèn)為true),決定是否向容器中添加ApplicationConversionServivce類實(shí)例。
applyInitializers(context)方法:在容器刷新之前,應(yīng)用所有的ApplicationContextInitializer接口實(shí)現(xiàn)類。遍歷執(zhí)行g(shù)etInitializers()方法得到的集合的initialize(context)方法初始化context容器,而for方法遍歷的ApplicationContextInitializer集合是應(yīng)用執(zhí)行SpringApplication構(gòu)造函數(shù)時(shí)getSpringFactoriesInstances()方法設(shè)置的。
listeners.contextPrepared(context)方法:遍歷執(zhí)行run方法里getRunListeners()得到的SpringApplicationRunListeners (SpringApplicationRunListener的集合)的contextPrepared(context)方法。執(zhí)行時(shí)機(jī)是,ApplicationContext容器完成創(chuàng)建和準(zhǔn)備之后,并且只調(diào)用一次。容器提供的SpringApplicationRunListener接口實(shí)現(xiàn)類是EventPublishingRunListener類,該類構(gòu)造函數(shù)生成的SimpleApplciationEventMulticaster類用來(lái)多路廣播發(fā)布SpringApplicationEvent接口的實(shí)現(xiàn)類。
logStartupInfo(context.getParent()==null)方法:記錄啟動(dòng)信息,其子類可以覆蓋該方法以添加附加信息。
logStartupProfileInfo(context)方法:記錄活動(dòng)配置文件信息。
context.getBeanFactory()方法:調(diào)用之前得到的ApplicationContext容器(默認(rèn)AnnotationConfigApplicationContext)的getBeanFactory()得到一個(gè)BeanFactory接口實(shí)現(xiàn)類(默認(rèn)DefaultListableBeanFactory類),轉(zhuǎn)換為ConfigurableListableBeanFactory類。
beanFactory.registerSingleton("springApplicationArguments", applicationArguments)方法:往容器中注冊(cè)ApplicationArguments接口實(shí)現(xiàn)類(默認(rèn)DefaultApplicationArguments類)的單例。
beanFactory.registerSingleton("springBootBanner", printedBanner)方法:向容器中添加printBanner(environment)方法返回的Banner類的單例。
((DefaultListableBeanFactory) beanfactory).setAllowBeanDefinitionOverring(this.allowBeanDefinitionOverriding)方法:設(shè)置BeanDefinitio覆蓋的開(kāi)關(guān)。
getAllSources()方法:獲取到應(yīng)用中所有的資源,即Bean的資源定位階段。該資源獲取的地點(diǎn)是,在SpringApplication構(gòu)造方法中產(chǎn)生,將run(MyApplication.class,args)中的MyApplication.class轉(zhuǎn)換存儲(chǔ)到LinkedHashSet<>中,并返回一個(gè)該資源Set的引用primarySources,在getAllSource()方法中獲取該primarySources。
Assert.notEmpty(sources, "Sources must not be empty")方法:斷言,判斷獲取到的sources是否為空,為空則停止應(yīng)用并返回錯(cuò)誤信息。
load(context, sources.toArray(new Object[0]))方法:從上面獲取的資源中加載bean進(jìn)入ApplicationContext中,即bean的加載階段。加載流程如下:
a.創(chuàng)建了BeanDefinitionLoader類,將傳入的context容器類型轉(zhuǎn)換為BeanDefinitionRegistry(該轉(zhuǎn)換局部生效);
b.創(chuàng)建了AnnotedBeanDefinitionReader類、AnnotationScopeMetadataResolver類、AnnotationBeanNameGenerator類以及ConditionEvaluator類;
c.創(chuàng)建XmlBeanDefinitionReader類、SimpleSaxErrorHandler類、XmlValidationModeDetector類以及NameThreadLocal類;
d. 創(chuàng)建了ClassPathBeanDefintionScannerl類以及GroovyBeanDefintionReader類(該類根據(jù)BeanDefinitionLoader中的isGroovyPresent()返回的布爾值決定是否創(chuàng)建);
e. 配置得到的BeanDefinitionLoader,并調(diào)用其load()方法,開(kāi)始加載BeanDefinition;
f. 根據(jù)傳入的資源的類型,決定加載Bean的方式以及BeanDefinition的類型(默認(rèn)為AnnotatedGenericBeanDefinition類);
g. 解析含有@Scope注解的BeanDefinition獲得其ScopeMetadata,并將scope添加到獲得的BeanDefinition中; h. 解析BeanDefinition獲得beanName;
i. 解析含有@Lazy注解的BeanDefinition,根據(jù)注解屬性值決定是否執(zhí)行setLazyInit()方法;
j. 遍歷函數(shù)輸入?yún)?shù)BeanDefinitionCustomizer(該接口是功能性接口)的集合,執(zhí)行其customizer(abd)方法;
k. 將剛才得到的BeanDefinition和beanName添加到BeanDefinitionHolder中;
l. 利用AOP框架創(chuàng)建作用域代理類,根據(jù)剛剛獲得的ScopeMetadata的getScopedProxyMode()方法返回的值,ScopedProxyMode 決定是否生成代理類并決定使用jdk代理生成代理類還是cglib生成代理類,并將得到的proxyDefinition代替之前的BeanDefinition添加到BeanDefinitionHolder中;
m. 向容器中注冊(cè)別名aliases和beanName;
listeners.contextLoaded(context)方法:遍歷執(zhí)行之前獲得的SpringApplicationRunListener接口實(shí)現(xiàn)類的contextLoaded(context)方法。
3.3 refreshContext(context)方法
調(diào)用AbstractApplicationContext的refresh()方法初始化DefaultListableBeanFactory工廠類。
(1)refreshContext(context)源碼
private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
}
}
}
調(diào)用的AbstractApplicationContext.refresh()源碼:
public void refresh() throws BeansException, IllegalStateException {
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();
}
}
}
(2)refreshContext(context)時(shí)序圖
下面時(shí)refreshContext(context)實(shí)際刷新容器的AbstractApplicationContext.refresh()的時(shí)序圖:
(3)方法分析
prepareRefresh()方法:為刷新準(zhǔn)備好容器。
a. 設(shè)置啟動(dòng)時(shí)間;
b. 設(shè)置標(biāo)示當(dāng)前context是否活動(dòng)的標(biāo)簽,active為true,closed為false,類型為AtomicBoolean;
c. 在context環(huán)境中初始化placeholder屬性源,默認(rèn)不做任何事情;
d. 驗(yàn)證所有標(biāo)記required的property都是可解析的;
e. 創(chuàng)建ApplicationEvent的集合,即LinkedHashSet<ApplicationEvent>。一旦多路廣播器可用(即prepareContext()階段得到的SimpleApplicationEventMulticaster類),便允許發(fā)布早期的ApplicationEnvent的集合;
obtainFreshBeanFactory()方法:告訴子類刷新內(nèi)部的bean工廠類。
a. 持有一個(gè)工廠類單例(默認(rèn)為DefaultListableBeanFactory),并依靠調(diào)用者通過(guò)工廠類單例或構(gòu)造方法注冊(cè)bean;
b. 將在AbstarctApplicationContext類生成的id和在DefaultListableBeanFatory生成指向自身的弱引用,存儲(chǔ)進(jìn)入一個(gè)由DefaultListableBeanFactory持有的Map中。
prepareBeanFactory(beanFactory)方法:準(zhǔn)備在context中要使用的beanFactory。配置工廠的標(biāo)準(zhǔn)context特性,比如容器的ClassLoader和后處理器。
a. 設(shè)置ClassLoader;
b. 設(shè)置BeanExpressionResolver接口實(shí)現(xiàn)類(為StandardBeanExpressionResolver類);
c. 設(shè)置PropertyEditorRegistrar接口實(shí)現(xiàn)類(為ResourceEditorRegistrar類);
e. 將ApplicationContextAwareProcessort添加進(jìn)入應(yīng)用獲得的工廠類實(shí)例持有的CopyOnWriteArrayList的一個(gè)引用中;
f. 將EnvironmentAware.class、EmbeddedValueResolverAware.class、ResourceLoaderAware.class、ApplicationEventPublisherAware.class、MessageSourceAware.class、MessageSourceAware.class 添加進(jìn)入應(yīng)用獲得的工廠類實(shí)例持有的HashSet的一個(gè)引用中;
g. 在普通工廠類中未注冊(cè)成為可解析類型的工廠類接口,而MessageSource作為bean被注冊(cè)(查找到并自動(dòng)裝配)。
h. 將BeanFactory.class、ResourceLoader.class、ApplicationEventPublisher.class、ApplicationContext.class 添加進(jìn)入應(yīng)用獲得的工廠類實(shí)例持有的ConcurrentHashMap<Class<?>, Object>的一引用中;
i. 注冊(cè)為了檢測(cè)內(nèi)部bean的早期后處理器,例如ApplicationListeners;
j. 將ApplicationListenerDetector的實(shí)例、LoadTimeWeaverAwareProcessor的實(shí)例添加進(jìn)入應(yīng)用獲得的工廠類實(shí)例持有的CopyOnWriteArrayList<BeanPostProcessor>的一個(gè)引用中;
k. 為持有的工廠類設(shè)置臨時(shí)可以匹配類型的ClassLoader,為ContextTypeMatchClassLoader類;
l. 配置默認(rèn)的環(huán)境單例bean;
postProcessBeanFactory(beanFactory)方法:允許容器子類中的bean工廠執(zhí)行后處理。該方法執(zhí)行時(shí)機(jī)時(shí),在ApplicationContext內(nèi)部bean工廠類標(biāo)準(zhǔn)初始化之后修改它,這時(shí)所有的BeanDifinition已經(jīng)被加載,但是還沒(méi)有被實(shí)例化。繼承這個(gè)方法允許某些AbstractApplicationContext類的子類中注冊(cè)特殊的類似BeanPostProcessors的類。默認(rèn)不做任何操作。
invokeBeanFactoryPostProcessors(beanFactory)方法:實(shí)例化所有已經(jīng)注冊(cè)的BeanFactoryPostProcessor的bean(實(shí)例化為單例),并遵循已給出的明確order值調(diào)用它,bean實(shí)例化與依賴注入開(kāi)始階段。
a. 實(shí)際調(diào)用了PostProcessorRegistrationDelegate的invokeBeanFactoryPostProcessor(beanfactory, getBeanFactoryPostProcessor)方法,在這個(gè)方法里完成了實(shí)例化和調(diào)用任務(wù);
b. 首先從方法中傳入的BeanFactoryPostProcessor集合參數(shù)中分辨出BeanDefinitionRegistryPostProcessor類,并分別裝入兩個(gè)ArrayList的容器中,方法持有容器的應(yīng)用;
c. 不實(shí)例化FactoryBean,并保留所有常規(guī)bean,不初始化它們,讓之后的bean工廠后處理器應(yīng)用他們;
d. 區(qū)分開(kāi)實(shí)現(xiàn)了PriorityOrdered類的BeanDefinitionRegistryPostProcessor類,將其實(shí)例化并裝入一個(gè)ArrayList的容器中;
e. 將剛剛獲得的BeanDefinitionRegistryPostProcessor的集合根據(jù)order的值排序;
f. 遍歷調(diào)用得到的BeanDefinitionRegistryPostProcessor類bean的postProcessBeanDefinitionBeanDefinitionRegistry(registry)方法;
g. 清除BeanDefinitionRegistryPostProcessor的ArrayList集合里的bean;
h. 區(qū)分開(kāi)實(shí)現(xiàn)了Ordered類的BeanDefinitionRegistryPostProcessor類,將其實(shí)例化并裝入一個(gè)ArrayList的容器中,并重復(fù)執(zhí)行一次e到g的步驟;
i. 最后,調(diào)用所有其他的BeanDefinitionRegistryPostProcessor,直到不再出現(xiàn)其他的BeanDefinitionRegistryPostProcessor,也重復(fù)執(zhí)行一次e到g的步驟;
j. 調(diào)用到目前為止的已處理的所有處理器的postProcessBeanFactory回調(diào);
g. 調(diào)用context實(shí)例注冊(cè)的factory的processor;
i. 清空緩存的合并的Bean定義,因?yàn)楹筇幚砥骺赡軙?huì)修改原始的元數(shù)據(jù),例如,替換值中的占位符。
registerBeanPostProcessors(beanFactory)方法:注冊(cè)攔截bean創(chuàng)建的bean處理器。
a. 向context持有的bean factory注冊(cè)BeanPostProcessorChecker,該檢查器其用來(lái)記錄在BeanPostProcessor實(shí)例化期間創(chuàng)建bean時(shí)的info信息,即當(dāng)所有BeanPostProcessor都不對(duì)bean進(jìn)行后處理操作時(shí)記錄。
b. 將所有的BeanPostProcessor按照實(shí)現(xiàn)PriorityOrdered、Oedered和剩下的區(qū)別,分別添加進(jìn)三個(gè)ArrayList容器;
c. 對(duì)剛剛獲得ArrayList分別根據(jù)order值進(jìn)行排序,最后整合后注冊(cè)進(jìn)入bean factory中;
d. 重新注冊(cè)后處理器,用于檢測(cè)像ApplicationListener的內(nèi)部類,將其移動(dòng)到后處理器鏈的末端,用于提取代理等作用;
initMessageSource()方法:為context初始化message源。從容器中獲得MessageSource的bean,若沒(méi)有定義,則會(huì)調(diào)用父類的MessageSource(無(wú)值,并會(huì)打印出沒(méi)有message source的日志信息)。此例為單例。
initApplicationEventMulticaster()方法:實(shí)例化應(yīng)用事件多路廣播器ApplicationEventMulticaster。從容器中獲取ApplicationEventMulticaster的bean,若沒(méi)有注冊(cè),則注入SimpleApplicationEventMulticaster類的bean。
onRefresh()方法:該方法使模板方法,在特定的context子類中初始化其他特殊的bean。
a. 在GenericWebApplicationContext、StaticWebApplicationContext中,該方法初始化了context的ThemeSource;
b. 在ServletWebServerApplicationContext、ReactiveWebServerApplicationContext中,該方法初始化類context的TnemeSource并創(chuàng)建了一個(gè)ServerManager。
registerListeners()方法: 檢測(cè)監(jiān)聽(tīng)器并注冊(cè)它們。向剛才獲得的ApplicationEventMulticaster中注冊(cè)實(shí)現(xiàn)了ApplicationListener的監(jiān)聽(tīng)器bean,但不影響那一些可以不用生成bean就可以加載的監(jiān)聽(tīng)器。
finishBeanFactoryInitialization(beanFactory)方法:實(shí)例化所有剩下的單例bean(沒(méi)有執(zhí)行l(wèi)azy初始化)。
a. 初始化context的類型轉(zhuǎn)換服務(wù);
b. 如果之前沒(méi)有任何注冊(cè)的bean后處理器(比如,一個(gè)PropertyPlaceholderConfigurer的bean),則注冊(cè)一個(gè)默認(rèn)的內(nèi)嵌的值解析器,在這一觀點(diǎn)下,優(yōu)先解析注解屬性值;
c. 盡早初始化LoadTimeWeaverAwarebean,以便盡早注冊(cè)它們的轉(zhuǎn)換器;
d. 停止使用臨時(shí)的類型匹配類加載器;
e. 允許緩存所有的bean定義元數(shù)據(jù),不需要進(jìn)一步修改了;
f. 實(shí)例化所有剩下的單例(沒(méi)有Lazy初始化的bean);
finishRefresh()方法:發(fā)布相應(yīng)的事件。
a. 清除context級(jí)別的資源緩存(例如,掃描得來(lái)的ASM元數(shù)據(jù));
b. 為context初始化生命周期處理器;
c. 首先向生命周期處理器傳播刷新;
d. 發(fā)布最后的事件(ContextRreshedEvent);
e. 若激活的話,便參與LiveBeansView類的mbean,。
destroyBeans()方法:摧毀已經(jīng)創(chuàng)建的bean,避免懸空資源。
cancelRefresh(ex)方法:取消刷新,設(shè)置active變量為false。
resetCommonCaches()方法:重置Spring中的內(nèi)省緩存,因?yàn)閼?yīng)用可能不再需要單例bean的元數(shù)據(jù)了。
a. 清除聲明方法緩存,清除聲明變量緩存(ConcurrentReferenceHashMap數(shù)據(jù)類型);
b. 清除注解相關(guān)緩存;
c. 清除可解析類型相關(guān)緩存;
d. 清除相關(guān)類加載器緩存(ConcurrentHashMap)。
四、總結(jié)
到此為止,分析了Spring Boot應(yīng)用啟動(dòng)過(guò)程run方法中,自己比較感興趣的三個(gè)方法,再以此延伸又接觸到了更多的相關(guān)類和相關(guān)方法,重新復(fù)習(xí)了許多java知識(shí)(繼承、重載、重寫(xiě)、接口、反射以及設(shè)計(jì)模式)。雖然自己還沒(méi)有能力寫(xiě)出這么優(yōu)美的框架,但是從分析源碼中自己學(xué)習(xí)到了不少的框架設(shè)計(jì)知識(shí),接觸到比較底層的設(shè)計(jì)思想。不盡人意的是,有些方法和重要類的分析由于自己能力有限,解釋得還有些模糊,決定在接下來(lái)的文章里將這些遺憾彌補(bǔ)回來(lái)。歡迎指正!