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

编程

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

下面对每个ApplicationListener做解释

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;

}

environmentPrepared

下面对每个ApplicationListener做解释

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启动行为。

以上是 SpringBoot源码分析启动流程SpringApplication实例化与参数加载 的全部内容, 来源链接: utcz.com/z/511995.html

回到顶部