SpringBoot源码分析启动流程准备ApplicationContext
上一篇:SpringBoot源码分析-启动流程-SpringApplication实例化与参数加载
前面完成了参数封装和日志框架logback的初始化,紧接着打印Banner(用户可以使用spring.banner.image.location自定义),再然后就是实例化ApplicationContext(AnnotationConfigServletWebServerApplicationContext)。
概要
由initializer完成了MetadataReaderFactoryBean注册,读取并调用自定义的initializer,注册"自动配置报告生成器",注册"配置警告后置处理器"
prepareContext
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
//将ApplicationConverterService.getInstance()初始化好的Converters添加到ApplicationContext当中
postProcessApplicationContext(context);
/**
* 1.SharedMetadataReaderFactoryContextInitializer: 添加了CachingMetadataReaderFactoryPostProcessor,它会注册一个SharedMetadataReaderFactoryBean,
* 并且将它对应的beanName设置为ConfigurationClassPostProcessor对应BeanDefinition的property:definition.getPropertyValues().add("metadataReaderFactory", new RuntimeBeanReference(BEAN_NAME));
* 2.DelegatingApplicationContextInitializer: 通过在environment中找到的context.initializer.classes,再去调用配置的initializer中的initialize方法
* 3.ContextIdApplicationContextInitializer: 在DefaultListableBeanFactory当中注册了一个
* key=org.springframework.boot.context.ContextIdApplicationContextInitializer$ContextId
* value=ContextIdApplicationContextInitializer$ContextId@xxxx的单例Bean
* 4.ConditionEvaluationReportLoggingListener: 给AnnotationConfigServletWebServerApplicationContext.ApplicationListeners添加了一个ConditionEvaluationReportListener,并且如果容器中没有名为"autoConfigurationReport"的bean,会手动注入一个
* 5.ConfigurationWarningsApplicationContextInitializer: 给AnnotationConfigServletWebServerApplicationContext添加了ConfigurationWarningsPostProcessor,
* 用于扫描当前项目中一般性的配置错误,并生成报告告知用户。比如:没有使用@ComponentScan注解的check。
* 6.ServerPortInfoApplicationContextInitializer: 将自己添加到AnnotationConfigServletWebServerApplicationContext.ApplicationListeners当中
*
*/
applyInitializers(context);
/**
* 发布ApplicationContextInitializedEvent事件给ApplicationListeners
*
*
*
*
*
*/
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//将命令行参数PropertySource注册为一个单例Bean
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
//将banner也注册到容器当中
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
//将启动类注册到BeanDefinitionMaps当中
load(context, sources.toArray(new Object[0]));
/**
* 1.给ApplicationContext添加配置的ApplicatioListener
* 2.发布ApplicationPreparedEvent事件给Listener,
* 这当中会有一个ConfigFileApplicationListener会将PropertySourceOrderingPostProcessor添加到ApplicationContext当中
* PropertySourceOrderingPostProcessor:里面包含了很多配置文件(application.yml或者application.property)读取路径、配置文件默认名称,配置文件重排序等等
*/
listeners.contextLoaded(context);
}
postProcessApplicationContext
将ApplicationConverterService.getInstance()初始化好的Converters添加到ApplicationContext当中
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {。。。。省略部分代码。。。。
if (this.addConversionService) {//被DefaultListableBeanFactory设置Converters
context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
}
}
applyInitializers
下面对每个initializer做解释
SharedMetadataReaderFactoryContextInitializer
往ApplicationContext中添加一个CachingMetadataReaderFactoryPostProcessor,该PostProcessor在Spring容器刷新的时候会注册一个SharedMetadataReaderFactoryBean,它是一个FactoryBean,当从容器获取的时候会调用它的getObject方法返回一个ConcurrentReferenceCachingMetadataReaderFactory对象。
说这么多,ConcurrentReferenceCachingMetadataReaderFactory有什么用喃,字面上看应该是与元数据读取有关系。因为有缓存设计,所以它才会设计为Concurrent的
public void initialize(ConfigurableApplicationContext applicationContext) {//给Context添加一个CachingMetadataReaderFactoryPostProcessor
applicationContext.addBeanFactoryPostProcessor(new CachingMetadataReaderFactoryPostProcessor());
}
DelegatingApplicationContextInitializer
看到它,立即想起了在前面看到的DelegatingApplicationListener,是的,该类提供了一个扩展点,可以让开发人员开发自己的ApplicationContextInitializer,通过配置文件注册并让其生效
public void initialize(ConfigurableApplicationContext context) {ConfigurableEnvironment environment = context.getEnvironment();
/**
* 在environment中找"context.initializer.classes"对应的值
*/
List<Class<?>> initializerClasses = getInitializerClasses(environment);
if (!initializerClasses.isEmpty()) {
/**
* 使用反射实例化对应的类,然后调用它们的initialize方法
*/
applyInitializerClasses(context, initializerClasses);
}
}
ContextIdApplicationContextInitializer
往容器中注册一个ContextId
public void initialize(ConfigurableApplicationContext applicationContext) {/**
* 从environment当中获取spring.application.name作为ContextId的ID属性值,否则就是默认的"application"作为ID属性值
*/
ContextId contextId = getContextId(applicationContext);
//将ContextId的ID属性值设置为当前ApplicationContext的id
applicationContext.setId(contextId.getId());
/**
* 往容器中注册一个名为"org.springframework.boot.context.ContextIdApplicationContextInitializer$ContextId"的ContextId
*/
applicationContext.getBeanFactory().registerSingleton(ContextId.class.getName(), contextId);
}
ConditionEvaluationReportLoggingListener
打印自动配置的报告
public void initialize(ConfigurableApplicationContext applicationContext) {this.applicationContext = applicationContext;
//往容器中添加一个ConditionEvaluationReportListener对应的ApplicationListener
applicationContext.addApplicationListener(new ConditionEvaluationReportListener());
if (applicationContext instanceof GenericApplicationContext) {
//将之前已经初始化到容器中的ConditionEvaluationReport拿出来赋值给this.report
this.report = ConditionEvaluationReport.get(this.applicationContext.getBeanFactory());
}
}
比如:在application.yml中配置如下
logging:level:
root: debug
当执行完ConditionEvaluationReportLoggingListener的如下代码后在控制台会输出
public void logAutoConfigurationReport(boolean isCrashReport) {。。。省略代码。。。。
if (!this.report.getConditionAndOutcomesBySource().isEmpty()) {
if (this.getLogLevelForReport().equals(LogLevel.INFO)) {
。。。省略代码。。。。
}
else {
if (this.logger.isDebugEnabled()) {
this.logger.debug(new ConditionEvaluationReportMessage(this.report));
}
else if (isCrashReport) {
logMessage("debug");
}
}
}
}
============================CONDITIONS EVALUATION REPORT
============================
Positive matches:
-----------------
CodecsAutoConfiguration matched:
- @ConditionalOnClass found required class "org.springframework.http.codec.CodecConfigurer" (OnClassCondition)
CodecsAutoConfiguration.JacksonCodecConfiguration matched:
- @ConditionalOnClass found required class "com.fasterxml.jackson.databind.ObjectMapper" (OnClassCondition)
。。。。。。省略若干行。。。。。。。。。。。。。
Exclusions:
-----------
None
Unconditional classes:
----------------------
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration
org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration
ConfigurationWarningsApplicationContextInitializer
往ApplicationContext中添加了一个BeanDefinitionRegistryPostProcessor,该PostProcessor默认添加了一个ComponentScanPackageCheck,在容器刷新的时候会调用它的PostProcessBeanDefinitionRegistry方法,校验是否有类添加了@ComponentScan注解
ConfigurationWarningsApplicationContextInitializer.initialize
public void initialize(ConfigurableApplicationContext context) {/**
* 添加PostProcessor
*/
context.addBeanFactoryPostProcessor(new ConfigurationWarningsPostProcessor(getChecks()));
}
ConfigurationWarningsApplicationContextInitializer.ConfigurationWarningsPostProcessor.postProcessBeanDefinitionRegistry
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {/**
* 默认调用ComponentScanPackageCheck的getWarning方法,检查是否有类使用了@ComponentScan注解
*/
for (Check check : this.checks) {
String message = check.getWarning(registry);
if (StringUtils.hasLength(message)) {
warn(message);
}
}
}
ServerPortInfoApplicationContextInitializer
将自己添加到ApplicationContext.applicationListener列表当中,当容器刷新完成,并且Tomcat server启动成功之后往容器中添加一个名为"server.ports"的MapPropertySource,里面包含了Tomcat的启动端口
到此为止ApplicationContext当中已经有了两个ApplicationListener
contextPrepared
下面对每个ApplicationListener做解释
BackgroundPreinitializer
呵呵,什么事情也没
DelegatingApplicationListener
呵呵,什么事情也没
contextLoaded
下面对每个ApplicationListener做解释
CloudFoundryVcapEnvironmentPostProcessor
logger.switchTo(CloudFoundryVcapEnvironmentPostProcessor.class);
立即输出与延迟输出切换
ConfigFileApplicationListener
添加了一个PropertySourceOrderingPostProcessor后置处理器,看名字就应该就是对environment中的PropertySource进行排序
LoggingApplicationListener
将在前面实例化好的logback日志框架对象以"springBootLoggingSystem"名称注册到ApplicationContext当中
BackgroundPreinitializer
什么事情也没做
DelegatingApplicationListener
什么事情也没做
看源码一定要坚持,当你慢慢对框架的认识有了主题轮廓之后,看的速度真的越来越快。
以上是 SpringBoot源码分析启动流程准备ApplicationContext 的全部内容, 来源链接: utcz.com/z/511991.html