SpringBoot源码分析-启动流程-SpringApplication实例化与参数加载

穿精又带淫゛_ 提交于 2019-12-23 19:05:28

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

上一篇:SpringBoot源码分析-编译环境与新建测试模块

标题是真的不好取啊,如果弄一篇博客写完整个启动流程又太长,而标题用1、2、3感觉可读性又不行,思来想去标题抓点重点,然后在文章开头用概要描述一下

概要

本文描述SpringApplication实例化期间加载spring.factories文件中的相关配置,并完成从系统、命令行、application.yml等渠道完成参数的封装,还有一个重要的功能就是完成了logback日志框架的初始化操作

SpringBootApplication实例化

实例化做了非常基础的工作,从spring.factories当中找到ApplicatioContextInitializer和ApplicationListener并加载完成,它们在后面的启动过程中会在不同阶段做不同事情。特别是ApplicationListener的工作方式和Tomcat源码当中的fireLifeycleListenerEvent工作方式很雷同

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        /**
         * 从类路径下所有META-INFO/spring.factories中找出所有的ApplicatioContextInitializer配置,使用构造函数反射实例化对象
         * 比较典型的:
         *
         */
        setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
        /**
         * 从类路径下所有META-INFO/spring.factories中找出所有的ApplicationListener配置,使用构造函数反射实例化对象
         * 比较典型的:
         *
         */
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        //推断mainApplicationClass
        this.mainApplicationClass = deduceMainApplicationClass();
    }

开始执行SpringApplication.run

开启一个秒表用以记录启动耗时,接着使用spring.factories配置的ApplicationListeners处理ApplicationStartingEvent事件完成很多事情,比如默认的TypeConverters,MessageConverters,Validators等等,然后创建熟悉的environment对象,并将命令行参数封装到environment.propertySource当中,最后使用前面的准备好的东西创建ApplicationContext。

下面的这段代码执行完成,SpringBoot就算启动完成了,看上去是不是很简单,但一眼看不见的spring.factories中的配置类却是相当的多,这些类由不同的事件触发被调用。看到这种代码想起了Tomcat源码,也是在server.xml中配置了不少的Listener进行处理,不同的事件干不同阶段的事情。

try {
            //封装main方法的参数
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            //处理参数,并将environment与Application绑定
            ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
            //在environment当中设置spring.beaninfo.ignore=true
            configureIgnoreBeanInfo(environment);
            //里面可以自定义banner
            Banner printedBanner = printBanner(environment);
            /**
             * 利用反射实例化了一个AnnotationConfigServletWebServerApplicationContext
             */
            context = createApplicationContext();
            exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);
            /**
             * 1.调用所有ApplicationInitializer.initialize方法
             * 2.添加一些配置Bean到容器中,如:命令行参数Bean,Banner
             * 3.将启动类注册到BeanDefinitionMaps当中
             * 4.将ApplicationListeners添加到ApplicationContext
             */
            prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            /**
             * 刷新ApplicationContext,也就是Spring实例化Bean
             * ConfigurationClassPostProcessor会解析到BootApplication上面的@SpringBootApplication中的
             * @Import(AutoConfigurationPackages.Registrar.class)
             * @Import(AutoConfigurationImportSelector.class) 它实现了DeferredImportSelector,当处理完普通的@Configuration就会按组解析并导入BeanDefinition,大体逻辑如下:
             * ——将spring.factories中EnableAutoConfiguration组的配置项与spring-autoconfigure-metadata.properties中的配置项匹配,并按照spring.factories
             * ——中的AutoConfigurationImportFilter组的过滤器进行过滤,最终得到需要自动配置的类。。。。这些配置类都是使用了@Configuration或者再加上@Import
             * ——再加上一些@Conditional实现有条件配置,这就是SpringBoot自动配置的关键
             *
             * 总结:
             * ConfigurationClassParser使用AutoConfigurationImportSelector.class选择性的加载spring.factories中配置的内容,具体逻辑是:
             * ——spring针对@Import的4种方式:普通的@Import @Import(Xxxx implements ImportSelector) @Import(Xxx implement DefferImportSelector)
             * ——@Import(Xxx implements ImportBeanDefinitionRegistrar),它们几乎是所有的Starter实现自动配置的关键点
             *
             */
            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);
        }

上面的代码包含了整个SpringBoot启动全过程,此篇文章只说一些准备阶段工作

listeners.starting()

LoggingApplicationListener

设置SpringBoot的默认日志框架为logback

private void onApplicationStartingEvent(ApplicationStartingEvent event) {
        /**
         * 反射实例化LogbackLoggingSystem
         */
        this.loggingSystem = LoggingSystem.get(event.getSpringApplication().getClassLoader());
        /**
         * 安装logback日志框架对应的handler
         */
        this.loggingSystem.beforeInitialize();
    }

loggingSystem.beforeInitialize()

public void beforeInitialize() {
        LoggerContext loggerContext = getLoggerContext();
        /**
         * 如果loggerContext中已经配置有org.springframework.boot.logging.LoggingSystem对应的logger,则返回
         * 否则
         */
        if (isAlreadyInitialized(loggerContext)) {
            return;
        }
        /**
         * 配置与安装slf4j日志框架的handler
         */
        super.beforeInitialize();
        /**
         * 添加1个内容为FilterReply.DENY的TurboFilter,目的是拒绝日志打印请求
         */
        loggerContext.getTurboFilterList().add(FILTER);
    }

BackgroundPreinitializer

Springboot默认将耗时的初始化任务使用后台线程先执行,这也叫做预初始化,设置系统属性spring.backgroundpreinitializer.ignore=true可以禁用该机制,但是禁用该机制,对应初始化由前台线程完成

public void onApplicationEvent(SpringApplicationEvent event) {
        if (!Boolean.getBoolean(IGNORE_BACKGROUNDPREINITIALIZER_PROPERTY_NAME)
                && event instanceof ApplicationStartingEvent && preinitializationStarted.compareAndSet(false, true)) {
            performPreinitialization();
        }
        if ((event instanceof ApplicationReadyEvent || event instanceof ApplicationFailedEvent)
                && preinitializationStarted.get()) {
            try {
                preinitializationComplete.await();
            }
            catch (InterruptedException ex) {
                Thread.currentThread().interrupt();
            }
        }
    }
private void performPreinitialization() {
        try {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    //为应用配置默认的converters/converterFactorys 和 formatters,比如:NumberToNumberConverterFactory、NumberToCharacterConverter
                    runSafely(new ConversionServiceInitializer());
                    //验证器
                    runSafely(new ValidationInitializer());
                    //消息转换器
                    runSafely(new MessageConverterInitializer());
                    //JMX MBean
                    runSafely(new MBeanFactoryInitializer());
                    //JACKSON
                    runSafely(new JacksonInitializer());
                    //字符集
                    runSafely(new CharsetInitializer());
                    preinitializationComplete.countDown();
                }
​
                public void runSafely(Runnable runnable) {
                    try {
                        runnable.run();
                    }
                    catch (Throwable ex) {
                        // Ignore
                    }
                }
​
            }, "background-preinit");
            thread.start();
        }
        catch (Exception ex) {
            preinitializationComplete.countDown();
        }
    }

 

DelegatingApplicationListener

对于ApplicationStartingEvent事件,没有处理逻辑

LiquibaseServiceLocatorApplicationListener

Liquibase是一个纯Java的数据库变更跟踪管理的开源工具,但是在Listener当中并没有使用相关功能,而是SpringBoot的SpringPackageScanClassResolver继承了Liquibase的DefaultPackageScanClassResolver类,使用它的扫描和加载类路径下包的类功能。

如果要使用的前提:在pom.xml中加入了如下依赖,里面需要把冲突的包给去掉,否则启动时会导致yml解析报错

<dependency>
            <groupId>org.liquibase</groupId>
            <artifactId>liquibase-core</artifactId>
            <version>3.6.3</version>
            <exclusions>
                <exclusion>
                    <groupId>org.yaml</groupId>
                    <artifactId>snakeyaml</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

最终有这么一段代码至关重要

private static class LiquibasePresent {
​
        public void replaceServiceLocator() {
            CustomResolverServiceLocator customResolverServiceLocator = new CustomResolverServiceLocator(
                    new SpringPackageScanClassResolver(logger));
            ServiceLocator.setInstance(customResolverServiceLocator);
        }
​
    }

说实话到底在什么时候会用到我也不知道 --!先把<dependency>干掉。。。

参数封装

封装命令行参数

ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

prepareEnvironment

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments) {
        // 看到了和SpringMVC一模一样的environment对象,里面比Spring多了servletConfigInitParams和servletContextInitParams
        ConfigurableEnvironment environment = getOrCreateEnvironment();
        /**
         * 1.初始化不同数据类型的Converter
         * 2.解析并封装命令行参数到environment当中
         * 3.从系统参数、环境变量、启动命令行参数中获取spring.profiles.active=xxx设置Active Profile
         */
        configureEnvironment(environment, applicationArguments.getSourceArgs());
        //对environment进行跟踪
        ConfigurationPropertySources.attach(environment);
        /**
         * 发布ApplicationEnvironmentPreparedEvent事件给ApplicationListeners
         * ——ConfigFileApplicationListener 会将spring-boot-autoconfigure模块中的META-INFO/spring.factories中 "# Environment Post Processors"
         *     注释下的PostProcessor添加到ApplicationContext中
         * ——AnsiOutputApplicationListener
         * ——LoggingApplicationListener
         * ——BackgroundPreinitializer
         * ——ClasspathLoggingApplicationListener
         * ——DelegatingApplicationListener
         * ——FileEncodingApplicationListener
         *
         */
        listeners.environmentPrepared(environment);
        /**
         * 不知道这段代码有什么作用,DEBUG进去发现从environment中没找到spring.main配置,最终会去绑定一个null,并没有什么用
         * 我甚至将bindToSpringApplication()方法注释掉,重新跑工程,是正常的
         */
        bindToSpringApplication(environment);
        if (!this.isCustomEnvironment) {
            environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
                    deduceEnvironmentClass());
        }
        /**
         * 重新生成了configurationProperties,和生成之前只有cache变成了0,好像并没有多大用处
         * 将这行代码注释掉,程序一样是可以跑的
         */
        ConfigurationPropertySources.attach(environment);
        return environment;
    }

listeners.environmentPrepared

ConfigFileApplicationListener

将从spring.factories中加载EnvironmentPostProcessor,然后调用它们的postProcessEnvironment方法,最关键的就是加载application.yml和application-active.yml,完成这个动作之后Spring容器在初始化Bean的时候才能使用${}进行属性的注入。

private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
        //从spring.factories中加载EnvironmentPostProcessor
        List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
        //将当前对象也加入EnvironmentPostProcessor列表
        postProcessors.add(this);
        //排序
        AnnotationAwareOrderComparator.sort(postProcessors);
        /**
         * 调用EnvironmentPostProcessor.postProcessEnvironment方法,有如下4个PostProcessor被调用
         * ——SystemEnvironmentPropertySourceEnvironmentPostProcessor
         * 将environment中的systemEnvironment由SystemEnvironmentPropertySource类型替换成OriginAwareSystemEnvironmentPropertySource类型
         * ——SpringApplicationJsonEnvironmentPostProcessor
         * 在idea启动"vm options"中添加-Dspring.application.json={"deptName":"研发部"}或者在系统环境变量中添加SPRING_APPLICATION_JSON={"deptName":"研发部"},前者优先级比系统环境变量更高
         * 最终会在environment.propertySourceList当中新增一个JsonPropertySource
         * ——CloudFoundryVcapEnvironmentPostProcessor
         * 这个感觉应该不重要吧,会先判断environment当中是否有"VCAP_APPLICATION"/"VCAP_SERVICES"两种属性,如果也会添加一个名为"vcap"的PropertySource
         * ——ConfigFileApplicationListener:这个就相当重要了,就是熟悉的application.yml/properties
         * 先添加了一个名为"random"的RandomValuePropertySource
         * 然后会根据spring.factories当中配置的两个PropertiesPropertySourceLoader、YamlPropertySourceLoader加载Source,最终
         * 会将默认的application.yml和active profile对应的OriginTrackedMapPropertySource添加到environment.propertySource列表中
         * 比如:application.yml和application-dev.yml
         */
        for (EnvironmentPostProcessor postProcessor : postProcessors) {
            postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
        }
    }

该Listener执行完成之后SpringBoot所需的绝大部分属性都已经加载完成,当然不算@PropertySource添加的,如图:

AnsiOutputApplicationListener

用以支持在控制台输出彩色的日志信息,有三种模式

DETECT:根据环境自动决定是否开启彩色输出,这是SpringBoot的默认选项

ALWAYS:启用彩色输出

NEVER:禁止彩色输出

一些博客说直接在application.yml中修改spring.output.ansi.enabled=never可以关闭,经过测试发现是不行的,而且在vm options中添加也不行,但是可以在idea中的overried parameter中设置了是可以。

这样设置之后,日志输出就没有彩色了。。。但为什么在application.yml和vm options中设置了没效果喃?

先要找到在AnsiOuputApplicationListener里面什么地方设置的AnsiOutput.Enabled

public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
        ConfigurableEnvironment environment = event.getEnvironment();
        Binder.get(environment).bind("spring.output.ansi.enabled", AnsiOutput.Enabled.class)
                .ifBound(AnsiOutput::setEnabled);
        AnsiOutput.setConsoleAvailable(environment.getProperty("spring.output.ansi.console-available", Boolean.class));
    }

往里面DEBUG,会发现是在这段代码中找"spring.output.ansi.enabled"属性,如果找到了就会退出循环

for (ConfigurationPropertySource source : context.getSources()) {
            ConfigurationProperty property = source.getConfigurationProperty(name);
            if (property != null) {
                return property;
            }
        }

从代码上看,只要想办法在任何一个PropertySource当中添加了"spring.output.ansi.enabled"即可,但事实上是SpringBoot在初始化environment的时候从系统属性中默认读取了,也就是说systemProperties当中已经了,所以在其后面的PropertySource添加了是没有任何作用的,这也解释了为什么在application.yml中添加了不生效的原因。

但并没有解释为什么在vm options中添加了不生效的原因。。。据我的经验在vm options中添加了"spring.output.ansi.enabled=never"会在systemProperties中有这么一条Key-value键值对,但是事实上还是默认的"spring.output.ansi.enabled=always",所以在vm options中添加了并没有生效。

不过Idea提供了另外一个选项:override parameters,可以覆盖systemProperties中的Key-Value。

那么如何才能更改喃,观察上面的图发现,commandLineArgs在systemProperties前面,所以想办法在它里面加一条试试

注:Program arguments 使用"--"是添加键值对,不带"--"则是添加单个对象到list当中

运行程序

证明生效了

这个功能看似并没有多大重要,但是仔细想想,可以看出参数生效的规律:以参数最终在environment.propertySourceList找到的顺序为准,而这个顺序在添加的PropertySource的时候就按照一定规则决定了

LoggingApplicationListener

使用log configfile 完成LogBack的初始化

protected void initialize(ConfigurableEnvironment environment, ClassLoader classLoader) {
        //使用environment中的属性初始化loggingSystem
        new LoggingSystemProperties(environment).apply();
        this.logFile = LogFile.get(environment);
        if (this.logFile != null) {
            this.logFile.applyToSystemProperties();
        }
        //预设置LoggingLevel
        initializeEarlyLoggingLevel(environment);
        //使用日志配置文件初始化logingSystem
        initializeSystem(environment, this.loggingSystem, this.logFile);
        //使用日志配置文件中的LoggingLevel
        initializeFinalLoggingLevels(environment, this.loggingSystem);
        //注册shutdownHook,前提是在Environment中能够找到logging.register-shutdown-hook=true
        registerShutdownHookIfNecessary(environment, this.loggingSystem);
    }

 

BackgroundPreinitializer

没什么事情做

ClasspathLoggingApplicationListener

程序启动时打印classpath中所有的jar

[file:/D:/program%20files/Java/jdk1.8.0_161/jre/lib/charsets.jar, file:/D:/program%20files/Java/jdk1.8.0_161/jre/lib/deploy.jar, file:/D:/program%20files/Java/jdk1.8.0_161/jre/lib/ext/access-bridge-64.jar, file:/D:/program%20files/Java/jdk1.8.0_161/jre/lib/ext/cldrdata.jar, file:/D:/program%20files/Java/jdk1.8.0_161/jre/lib/ext/dnsns.jar, file:/D:/program%20files/Java/jdk1.8.0_161/jre/lib/ext/jaccess.jar, file:/D:/program%20files/Java/jdk1.8.0_161/jre/lib/ext/jfxrt.jar, file:/D:/program%20files/Java/jdk1.8.0_161/jre/lib/ext/localedata.jar, file:/D:/program%20files/Java/jdk1.8.0_161/jre/lib/ext/nashorn.jar, file:/D:/program%20files/Java/jdk1.8.0_161/jre/lib/ext/sunec.jar, file:/D:/program%20files/Java/jdk1.8.0_161/jre/lib/ext/sunjce_provider.jar, file:/D:/program%20files/Java/jdk1.8.0_161/jre/lib/ext/sunmscapi.jar, file:/D:/program%20files/Java/jdk1.8.0_161/jre/lib/ext/sunpkcs11.jar, file:/D:/program%20files/Java/jdk1.8.0_161/jre/lib/ext/zipfs.jar, file:/D:/program%20files/Java/jdk1.8.0_161/jre/lib/javaws.jar, file:/D:/program%20files/Java/jdk1.8.0_161/jre/lib/jce.jar, file:/D:/program%20files/Java/jdk1.8.0_161/jre/lib/jfr.jar, file:/D:/program%20files/Java/jdk1.8.0_161/jre/lib/jfxswt.jar, file:/D:/program%20files/Java/jdk1.8.0_161/jre/lib/jsse.jar, file:/D:/program%20files/Java/jdk1.8.0_161/jre/lib/management-agent.jar, file:/D:/program%20files/Java/jdk1.8.0_161/jre/lib/plugin.jar, file:/D:/program%20files/Java/jdk1.8.0_161/jre/lib/resources.jar, file:/D:/program%20files/Java/jdk1.8.0_161/jre/lib/rt.jar, file:/F:/projects/own/spring-boot-2.1.x/spring-boot-project/study-spring-boot/target/classes/, file:/F:/data/maven/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar, file:/F:/data/maven/repository/ch/qos/logback/logback-core/1.2.3/logback-core-1.2.3.jar, file:/F:/data/maven/repository/org/slf4j/slf4j-api/1.7.25/slf4j-api-1.7.25.jar, file:/F:/data/maven/repository/org/apache/logging/log4j/log4j-to-slf4j/2.11.2/log4j-to-slf4j-2.11.2.jar, file:/F:/data/maven/repository/org/apache/logging/log4j/log4j-api/2.11.2/log4j-api-2.11.2.jar, file:/F:/data/maven/repository/org/slf4j/jul-to-slf4j/1.7.29/jul-to-slf4j-1.7.29.jar, file:/F:/data/maven/repository/javax/annotation/javax.annotation-api/1.3.2/javax.annotation-api-1.3.2.jar, file:/F:/data/maven/repository/org/springframework/spring-core/5.1.12.RELEASE/spring-core-5.1.12.RELEASE.jar, file:/F:/data/maven/repository/org/springframework/spring-jcl/5.1.12.RELEASE/spring-jcl-5.1.12.RELEASE.jar, file:/F:/data/maven/repository/org/yaml/snakeyaml/1.23/snakeyaml-1.23.jar, file:/F:/data/maven/repository/com/fasterxml/jackson/datatype/jackson-datatype-jdk8/2.9.10/jackson-datatype-jdk8-2.9.10.jar, file:/F:/data/maven/repository/com/fasterxml/jackson/core/jackson-core/2.9.10/jackson-core-2.9.10.jar, file:/F:/data/maven/repository/com/fasterxml/jackson/datatype/jackson-datatype-jsr310/2.9.10/jackson-datatype-jsr310-2.9.10.jar, file:/F:/data/maven/repository/com/fasterxml/jackson/core/jackson-annotations/2.9.10/jackson-annotations-2.9.10.jar, file:/F:/data/maven/repository/com/fasterxml/jackson/module/jackson-module-parameter-names/2.9.10/jackson-module-parameter-names-2.9.10.jar, file:/F:/data/maven/repository/org/apache/tomcat/embed/tomcat-embed-core/9.0.29/tomcat-embed-core-9.0.29.jar, file:/F:/data/maven/repository/org/apache/tomcat/embed/tomcat-embed-el/9.0.29/tomcat-embed-el-9.0.29.jar, file:/F:/data/maven/repository/org/apache/tomcat/embed/tomcat-embed-websocket/9.0.29/tomcat-embed-websocket-9.0.29.jar, file:/F:/data/maven/repository/org/hibernate/validator/hibernate-validator/6.0.18.Final/hibernate-validator-6.0.18.Final.jar, file:/F:/data/maven/repository/javax/validation/validation-api/2.0.1.Final/validation-api-2.0.1.Final.jar, file:/F:/data/maven/repository/org/jboss/logging/jboss-logging/3.3.2.Final/jboss-logging-3.3.2.Final.jar, file:/F:/data/maven/repository/com/fasterxml/classmate/1.3.4/classmate-1.3.4.jar, file:/F:/data/maven/repository/org/springframework/spring-web/5.1.12.RELEASE/spring-web-5.1.12.RELEASE.jar, file:/F:/data/maven/repository/org/springframework/spring-beans/5.1.12.RELEASE/spring-beans-5.1.12.RELEASE.jar, file:/F:/data/maven/repository/org/springframework/spring-webmvc/5.1.12.RELEASE/spring-webmvc-5.1.12.RELEASE.jar, file:/F:/data/maven/repository/org/springframework/spring-aop/5.1.12.RELEASE/spring-aop-5.1.12.RELEASE.jar, file:/F:/data/maven/repository/org/springframework/spring-context/5.1.12.RELEASE/spring-context-5.1.12.RELEASE.jar, file:/F:/data/maven/repository/org/springframework/spring-expression/5.1.12.RELEASE/spring-expression-5.1.12.RELEASE.jar, file:/F:/projects/own/spring-boot-2.1.x/spring-boot-project/spring-boot-autoconfigure/target/classes/, file:/F:/projects/own/spring-boot-2.1.x/spring-boot-project/spring-boot/target/classes/, file:/D:/program%20files/IntelliJ%20IDEA%202019.1.4/lib/idea_rt.jar, file:/C:/Users/Administrator/.IntelliJIdea2019.1/system/groovyHotSwap/gragent.jar, file:/C:/Users/Administrator/.IntelliJIdea2019.1/system/captureAgent/debugger-agent.jar]

这个信息可以帮助我们找包冲突、明确包的版本等

DelegatingApplicationListener

从environment找context.listener.classes属性,通过这个属性开发人员可以开发ApplicationListener来干预SpringBoot的启动,而不需要去修改spring.factories配置文件。而context.listener.classes可以很方便的配置。

FileEncodingApplicationListener

如果在environment当中找到了spring.mandatory-file-encoding属性,那么要求environment中的"spring.mandatory-file-encoding"和"file-encoding"两者必须是一致的,否则会报错

bindToSpringApplication

真的不知道是干什么的,看代码逻辑是从environment中找spring.main配置,默认是没有的,则会对null值进行绑定,感觉像是什么也没有做。

我甚至将该方法注释掉,程序照样跑,有点怪怪。

我大胆的试了一下设置了一个spring.main=jagj,乱设置的值,报了一个不能将一个String类型转换为SpingApplication类型,有哪位大神知道的,请科普一下

总结

SpringBoot在完成spring.factories文件加载后,使用加载的initializer和ApplicationListener完成各渠道参数封装、logback日志框架的初始化,这样在后面实例化Bean的时候才能从environment当中找对应的值进行属性填充。

ApplicationListener的工作方式和Tomcat源码的LifecycleListener方式差不多,同一个Listener能处理多种事件,不同事件的行为不同。

对于高级开发者来说,可以通过DelegatingApplicationListener注册自定义的ApplicationListener干预SpringBoot启动行为。

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