SpringBoot2.2.6啟動(dòng)run方法之prepareContext
前言
此系列是針對(duì)springboot的啟動(dòng)柒爵,旨在于和大家一起來看看springboot啟動(dòng)的過程中到底做了一些什么事馁蒂。文中有不清楚或錯(cuò)誤的地方
歡迎留言指正靡菇。
源碼解讀進(jìn)度
首先我們的源碼閱讀進(jìn)度
public ConfigurableApplicationContext run(String... args) {
// 用于記錄啟動(dòng)時(shí)間
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 聲明Spring上下文
ConfigurableApplicationContext context = null;
// 聲明啟動(dòng)錯(cuò)誤回掉
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 設(shè)置jdk系統(tǒng)屬性java.awt.headless,默認(rèn)情況為true即開啟
configureHeadlessProperty();
// 裝飾者模式創(chuàng)建啟動(dòng)監(jiān)聽器(EventPublishingRunListener實(shí)例)
SpringApplicationRunListeners listeners = getRunListeners(args);
// 觸發(fā)ApplicationStartingEvent事件灵临,包括轉(zhuǎn)換器的初始化
listeners.starting();
try {
// 參數(shù)封裝潮瓶,也就是在命令行下啟動(dòng)應(yīng)用帶的參數(shù)
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 準(zhǔn)備環(huán)境:1、加載外部化配置的資源到environment晌梨;2桥嗤、觸發(fā)ApplicationEnvironmentPreparedEvent事件
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
// 配置spring.beaninfo.ignore,并添加到名叫systemProperties的PropertySource中仔蝌;默認(rèn)為true即開啟
configureIgnoreBeanInfo(environment);
// 打印banner圖
Banner printedBanner = printBanner(environment);
// 創(chuàng)建應(yīng)用上下文
context = createApplicationContext();
// 創(chuàng)建異常報(bào)告對(duì)象泛领,報(bào)告異常原因
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 初始化上下文,本文重點(diǎn)
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;
}
prepareContext做了哪些事情
首先看代碼掌逛,我們會(huì)在代碼上做相應(yīng)的注釋师逸,后面需要詳細(xì)分析的,前面都會(huì)標(biāo)注序號(hào)
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
// 給上下文對(duì)象設(shè)置環(huán)境對(duì)象豆混,給AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner設(shè)置環(huán)境對(duì)象
context.setEnvironment(environment);
// 1. 上下文后置處理篓像,包括向BeanFactory中注冊(cè)BeanNameGenerator和ConversionService
postProcessApplicationContext(context);
// 2. SpringApplication構(gòu)造器中初始化了各種ApplicationContextInitializer,循環(huán)調(diào)用他們的initialize方法
applyInitializers(context);
// 3. 發(fā)送ApplicationContextInitializedEvent事件
listeners.contextPrepared(context);
if (this.logStartupInfo) {
// 打印啟動(dòng)信息皿伺,包括pid员辩,用戶等
logStartupInfo(context.getParent() == null);
// 答應(yīng)profile信息
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
// 將ApplicationArguments注冊(cè)到容器中
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
// 將Banner注冊(cè)到容器中
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
// 設(shè)置不允許定義同名的BeanDefinition,重復(fù)注冊(cè)時(shí)拋出異常
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.lazyInitialization) {
// 如果懶加載鸵鸥,添加懶加載后置處理器
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
// 發(fā)送ApplicationPreparedEvent事件
listeners.contextLoaded(context);
}
下面詳細(xì)分析一下重要的幾個(gè)步驟
- postProcessApplicationContext
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
// BeanNameGenerator是生成bean名字的接口奠滑,用戶可以自己實(shí)現(xiàn)。用戶沒有設(shè)置自己的BeanNameGenerator的時(shí)候
// AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner的默認(rèn)實(shí)現(xiàn)是AnnotationBeanNameGenerator
// AnnotationBeanNameGenerator生成名稱規(guī)則是判斷注解中的value是否有值妒穴,沒有的話使用類名首字母小寫作為bean名
// 如果用戶自定義了beanNameGenerator宋税,prepareContext方法里面的load(context, sources.toArray(new Object[0]));這一步會(huì)設(shè)置
if (this.beanNameGenerator != null) {
// 默認(rèn)情況下不會(huì)走到這一步,將用戶自定義的beanNameGenerator注冊(cè)到beanfactory中
context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
this.beanNameGenerator);
}
// 默認(rèn)不走這一步
// 如果用戶設(shè)置了自己的resourceLoader讼油,會(huì)設(shè)置resourceLoader到GenericApplicationContext(SpringBoot默認(rèn)使用環(huán)境)
// 然后看一下GenericApplicationContext的getRoesource方法杰赛,
// @Override
// public Resource getResource(String location) {
// if (this.resourceLoader != null) {
// return this.resourceLoader.getResource(location);
// }
// return super.getResource(location);
// }
// 看到如果設(shè)置了resourceLoader就調(diào)用resourceLoader的getResource,否則調(diào)用
// GenericApplicationContext的父類DefaultResourceLoader的getResource方法
if (this.resourceLoader != null) {
if (context instanceof GenericApplicationContext) {
((GenericApplicationContext) context).setResourceLoader(this.resourceLoader);
}
if (context instanceof DefaultResourceLoader) {
((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader());
}
}
// 默認(rèn)會(huì)走該方法
// 上文中我們分析createApplicationContext的時(shí)候矮台,說過構(gòu)造器中初始化了DefaultListableBeanFactory實(shí)例對(duì)象
// 所以這里context.getBeanFactory()拿到的就是DefaultListableBeanFactory實(shí)例
// setConversionService設(shè)置個(gè)各種轉(zhuǎn)換器乏屯,比如:調(diào)用接口時(shí)傳了一個(gè)參數(shù)為age=“18”,
// 接口接收參數(shù)為Integer類型瘦赫,轉(zhuǎn)換器StringToNumberConverterFactory就會(huì)將String轉(zhuǎn)換為Integer
if (this.addConversionService) {
context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
}
}
- applyInitializers
protected void applyInitializers(ConfigurableApplicationContext context) {
// SpringApplication構(gòu)造器中初始化了各種ApplicationContextInitializer辰晕,getInitializers()方法把他們排序并放到Set中
// 1. DelegatingApplicationContextInitializer 從environment中獲取context.initializer.classes屬性,默認(rèn)為null确虱。
// 如果配置了數(shù)據(jù)并且是一個(gè)逗號(hào)分隔的列表含友,分別判斷是不是ApplicationContextInitializer的實(shí)現(xiàn),然后調(diào)用initialize方法.
// 2. SharedMetadataReaderFactoryContextInitializer 向beanFactoryPostProcessors(beanFactory后置處理器)中
// 添加一個(gè)CachingMetadataReaderFactoryPostProcessor。該后置處理器會(huì)在后面講到的refreshContext方法中會(huì)執(zhí)行唱较,
// 在執(zhí)行方法中向beanFactory中注冊(cè)一個(gè) BeanDefinition(SharedMetadataReaderFactoryBean)
// 然后向internalConfigurationAnnotationProcessor的BeanDefinition中注冊(cè)一個(gè)RuntimeBeanReference扎唾,在Bean實(shí)例化的
// 時(shí)候設(shè)置metadataReaderFactory為SharedMetadataReaderFactoryBean
// 3. ContextIdApplicationContextInitializer 設(shè)置ContextId,通過spring.application.name設(shè)置
// 4. ConfigurationWarningsApplicationContextInitializer 向beanFactoryPostProcessors(beanFactory后置處理器)中
// 添加一個(gè)ConfigurationWarningsPostProcessor南缓,作用是添加一下檢查。默認(rèn)有一個(gè)ComponentScanPackageCheck荧呐,作用是檢查@ComponentScan
// 掃描的包路徑是否合法
// 5. ServerPortInfoApplicationContextInitializer 添加一個(gè)ApplicationListener汉形。監(jiān)聽WebServerInitializedEvent事件,
// 向Environment中添加端口號(hào)local.sever.port
// 6. ConditionEvaluationReportLoggingListener 監(jiān)聽ContextRefreshedEvent和ApplicationFailedEvent事件倍阐,并做日志
for (ApplicationContextInitializer initializer : getInitializers()) {
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
initializer.initialize(context);
}
}