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

回到顶部