SpringBoot源码分析-启动流程-准备ApplicationContext

戏子无情 提交于 2019-12-23 21:13:08

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

上一篇: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

什么事情也没做

 

看源码一定要坚持,当你慢慢对框架的认识有了主题轮廓之后,看的速度真的越来越快。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!