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