SpringBoot版本:2.1.1 ==》启动流程分析汇总
接上篇博客 Spring Boot 2.1.1(九)启动流程分析之args参数的封装解析。
目录
3、发布ApplicationEnvironmentPreparedEvent事件
args参数封装执行完成以后,就是构建环境Environment,下面分析其运行流程。
public ConfigurableApplicationContext run(String... args) {
....
try {
//本篇内容从本行开始记录
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
//本篇内容记录到这,后续更新
....
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
}
构建环境
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
//创建环境,这里是根据web应用类型返回的,得到的是一个标准基于servlet的web环境
ConfigurableEnvironment environment = getOrCreateEnvironment();
//配置环境,参数是上一步创建的environment和原始args参数
configureEnvironment(environment, applicationArguments.getSourceArgs());
//发布事件ApplicationEnvironmentPreparedEvent
listeners.environmentPrepared(environment);
//将spring.main配置绑定到SpringApplication
bindToSpringApplication(environment);
//isCustomEnvironment默认为false,如果你通过SpringApplication对象的setEnvironment()方法设置了环境,该值变为true,直接跳过这里
//转换为标准环境
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader())
.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
1、创建ConfigurableEnvironment对象
private ConfigurableEnvironment getOrCreateEnvironment() {
//如果你通过SpringApplication对象的setEnvironment()方法设置了环境,则直接返回该环境
if (this.environment != null) {
return this.environment;
}
//根据webApplicationType选择
switch (this.webApplicationType) {
case SERVLET:
//标准的servlet环境
return new StandardServletEnvironment();
case REACTIVE:
//标准的响应式web环境
return new StandardReactiveWebEnvironment();
default:
//标准环境
return new StandardEnvironment();
}
}
返回的标准servlet环境如下:
StandardServletEnvironment {
activeProfiles=[],
defaultProfiles=[default],
propertySources[
StubPropertySource{name='servletConfigInitParams'},
StubPropertySource {name='servletContextInitParams'},
MapPropertySource {name='systemProperties'},
SystemEnvironmentPropertySource {name='systemEnvironment'}]
}
2、配置环境
protected void configureEnvironment(ConfigurableEnvironment environment,
String[] args) {
//是否添加转换服务,后面在必要时转换环境
if (this.addConversionService) {
//得到共享实例
ConversionService conversionService = ApplicationConversionService
.getSharedInstance();
//set
environment.setConversionService(
(ConfigurableConversionService) conversionService);
}
//配置属性源,包括默认配置和命令行配置,默认配置使用SpringApplication的setDefaultProperties(Map<String, Object> defaultProperties)方法设置
configurePropertySources(environment, args);
//设置配置文件
configureProfiles(environment, args);
}
2.1、配置属性源
protected void configurePropertySources(ConfigurableEnvironment environment,
String[] args) {
//得到属性源,即返回的标准环境中的propertySources,上面贴出来了
MutablePropertySources sources = environment.getPropertySources();
//判断默认配置是否为空,不为空就添加到source里即propertySources,name为defaultProperties
//通过application.setDefaultProperties(defaultProperties);设置
if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
sources.addLast(
new MapPropertySource("defaultProperties", this.defaultProperties));
}
//判断原始args参数长度是否大于0,addCommandLineProperties 默认为true
if (this.addCommandLineProperties && args.length > 0) {
//得到name:commandLineArgs,name值前面封装args参数说过了就不解释了
String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
//判断source是否存在commandLineArgs,哈哈哈,肯定不存在,propertySources的内容上面都贴出来了
if (sources.contains(name)) {
//通过commandLineArgs得到配置
PropertySource<?> source = sources.get(name);
//new一个CompositePropertySource,是PropertySource的子类,所以构造方法里传一个name知道啥意思了吧,前面介绍过PropertySource了
//该类中有一个Set<PropertySource<?>>
CompositePropertySource composite = new CompositePropertySource(name);
//添加到set集合
composite.addPropertySource(new SimpleCommandLinePropertySource(
"springApplicationCommandLineArgs", args));
composite.addPropertySource(source);
//用composite替换原 source中name为commandLineArgs的配置
sources.replace(name, composite);
}
else {
//不存在就new一个SimpleCommandLinePropertySource实例添加
sources.addFirst(new SimpleCommandLinePropertySource(args));
}
}
}
2.2、配置配置文件
protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
environment.getActiveProfiles(); // 确保已初始化
// But these ones should go first (last wins in a property key clash)
//this.additionalProfiles为set集合,使用application.setAdditionalProfiles(profiles)可以设置,即设置启用的配置文件
Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
//将配置项设置的值也添加到set集合
profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
//设置ActiveProfiles
environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}
public String[] getActiveProfiles() {
return StringUtils.toStringArray(doGetActiveProfiles());
}
可以看到得到的配置文件就是配置项spring.profiles.active配置的。会判断this.activeProfiles是否为空,如果你是通过context.getEnvironment().setActiveProfiles(profiles)方法设置的,那就直接返回了。

3、发布ApplicationEnvironmentPreparedEvent事件
事件的具体发布过程就不详细解释了,前面解释过了。
listeners.environmentPrepared(environment);
检索出来的Listener有七个。

同样简单介绍下Lintener的onApplication()方法执行了什么操作。
ConfigFileApplicationListener:得到EnvironmentPostProcessor接口的实现类实例,List集合,并添加本身,根据order排序,循环执行postProcessEnvironment方法。
得到的EnvironmentPostProcessor接口实现类有三个,加上ConfigFileApplicationListener,一共四个,如下。

SystemEnvironmentPropertySourceEnvironmentPostProcessor:从标准环境中得到name为systemEnvironment的PropertySource,然后将SystemEnvironmentPropertySource替换为OriginAwareSystemEnvironmentPropertySource,用于提供从系统环境加载的项对原始属性名的访问。即可以通过原始属性名的到属性值。
SpringApplicationJsonEnvironmentPostProcessor:循环数组String[] CANDIDATES={spring.application.json,SPRING_APPLICATION_JSON},确认propertySource中是否存在这两个系统属性,如果存在new一个JsonPropertyValue对象,该类为内部类,将name和value封装,如果不存在返回空,然后过滤是否为空,找到第一个确认是否有这个值,如果有就解析json封装成JsonPropertySource并添加到环境(environment)中,如果不存在不执行任何操作。


![]()

CloudFoundryVcapEnvironmentPostProcessor:判断环境中是否存在VCAP_APPLICATION或者VCAP_SERVICES。如果有就添加Cloud Foundry的配置;没有就不执行任何操作。
ConfigFileApplicationListener:得到PropertySourceLoader的实现类PropertiesPropertySourceLoader、YamlPropertySourceLoader,然后加载配置文件,包括application.properties/yml(或yaml)/xml以及application-{dev,prod,test}.properties/yml(或yaml)/xml
AnsiOutputApplicationListener:将spring.output.ansi.enabled的值跟环境自绑定,再根据绑定值配置ANSI输出。
有三个值:
DETECT:尝试检测ANSI着色功能是否可用,默认是这个
ALWAYS:启用ANSI彩色输出
NEVER:禁用ANSI彩色输出
然后根据spring.output.ansi.console-available的值设置System.Console()是否可用。
LoggingApplicationListener:调用initialize()方法完成日志系统的初始化,前面ApplicationStartingEvent事件发布的时候已经调用了beforeInitialize()方法。如果没有配置日志,则使用默认配置,默认情况下,日志输出只写入控制台。如果需要日志文件,logging.path和logging.file属性可以使用。
ClasspathLoggingApplicationListener:在调试级别记录线程上下文类加载器(TCCL)的类路径,对ApplicationEnvironmentPreparedEvent和ApplicationFailedEvent做出反应。

BackgroundPreinitializer:貌似在这一步没做什么事,在ApplicationStartingEvent事件发布的时候已经执行了。
DelegatingApplicationListener:如果是ApplicationEnvironmentPreparedEvent事件,在getListeners方法中会得到配置项"context.listener.classes"设置的listener,得到实例对象添加到多播器multicaster中。否则判断多播器multicaster是否为空,不为空继续广播事件。
FileEncodingApplicationListener:默认情况下不起什么作用,也就是啥都不执行。但是如果spring.mandatory_file_encoding的值和file.encoding属性值不同时会引发报错。
4、将环境绑定到SpringApplication
protected void bindToSpringApplication(ConfigurableEnvironment environment) {
try {
Binder.get(environment).bind("spring.main", Bindable.ofInstance(this));
}
catch (Exception ex) {
throw new IllegalStateException("Cannot bind to SpringApplication", ex);
}
}
5、将配置属性源绑定到Environment
public static void attach(Environment environment) {
Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
//得到PropertySources
MutablePropertySources sources = ((ConfigurableEnvironment) environment)
.getPropertySources();
//得到name为configurationProperties的配置源,现在是没有的,所以是null
PropertySource<?> attached = sources.get(ATTACHED_PROPERTY_SOURCE_NAME);
if (attached != null && attached.getSource() != sources) {
sources.remove(ATTACHED_PROPERTY_SOURCE_NAME);
attached = null;
}
if (attached == null) {
//new了一个ConfigurationPropertySourcesPropertySource,name为configurationProperties,source就是整个source,该类继承了PropertySource,构造方法参数啥用不多说了,添加到第一个
sources.addFirst(new ConfigurationPropertySourcesPropertySource(
ATTACHED_PROPERTY_SOURCE_NAME,
new SpringConfigurationPropertySources(sources)));
}
}
总结
整个prepareEnvironment()方法走完以后得到的Environment如下:
StandardServletEnvironment {
activeProfiles=[],
defaultProfiles=[default],
propertySources=[
ConfigurationPropertySourcesPropertySource {name='configurationProperties'},
StubPropertySource {name='servletConfigInitParams'},
StubPropertySource {name='servletContextInitParams'},
MapPropertySource {name='systemProperties'},
OriginAwareSystemEnvironmentPropertySource {name='systemEnvironment'},
RandomValuePropertySource {name='random'}]}
其中ApplicationEnvironmentPreparedEvent事件的发布主要做的就是加载配置文件,初始化日志系统。
用起来有多方便,debug看源码的时候都要还的.....
本文分析了SpringBoot启动过程中构建环境的流程,重点讲解了ApplicationEnvironmentPreparedEvent事件的发布,包括配置属性源、配置文件、EnvironmentPostProcessor的执行等,这些步骤涉及配置文件加载、日志系统初始化等关键操作。

480

被折叠的 条评论
为什么被折叠?



