SpringBoot启动流程分析之ApplicationEnvironmentPreparedEvent事件发布(四)
目录:
文章目录
- SpringBoot启动流程分析之ApplicationEnvironmentPreparedEvent事件发布(四)
- 构建环境
- 1、创建ConfigurableEnvironment对象
- 2、配置环境
- 2.1、配置属性源
- 2.2、配置配置文件
- 3、发布ApplicationEnvironmentPreparedEvent事件
- 4、将环境绑定到SpringApplication
- 5、将配置属性源绑定到Environment
- 六、SpringApplication.run调用两次
args参数封装执行完成以后,就是构建环境Environment,下面分析其运行流程。
org.springframework.boot.SpringApplication#run(java.lang.String…)
public ConfigurableApplicationContext run(String... args) {
try {
// 开始分析...
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
// 其余代码忽略了...
}catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
return context;
}
构建环境
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
listeners.environmentPrepared(environment);
bindToSpringApplication(environment);
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:
return new StandardServletEnvironment(); //标准的servlet环境
case REACTIVE:
return new StandardReactiveWebEnvironment(); //标准的响应式web环境
default:
return new StandardEnvironment(); //标准环境
}
}
从UML类图看出:StandardServletEnvironment 继承StandardEnvironment 实现ConfigurableWebEnvironment
StandardEnvironment 继承AbstractEnvironment 中构造函数中customizePropertySources()
在StandardServletEnvironment 中有重写:
/** Servlet context init parameters property source name: {@value}. */
public static final String SERVLET_CONTEXT_PROPERTY_SOURCE_NAME = "servletContextInitParams";
/** Servlet config init parameters property source name: {@value}. */
public static final String SERVLET_CONFIG_PROPERTY_SOURCE_NAME = "servletConfigInitParams";
/** JNDI property source name: {@value}. */
public static final String JNDI_PROPERTY_SOURCE_NAME = "jndiProperties";
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME));
propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));
if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME));
}
super.customizePropertySources(propertySources);
}
StandardEnvironment重写:
/** System environment property source name: {@value}. */
public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";
/** JVM system properties property source name: {@value}. */
public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(
new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
propertySources.addLast(
new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}
返回的标准servlet环境如下:
StandardServletEnvironment {
activeProfiles=[],
defaultProfiles=[default],
propertySources=[
StubPropertySource {name='servletConfigInitParams'},
StubPropertySource {name='servletContextInitParams'},
PropertiesPropertySource {name='systemProperties'},
SystemEnvironmentPropertySource {name='systemEnvironment'}
]
}
2、配置环境
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 开始分析
configureEnvironment(environment, applicationArguments.getSourceArgs());
listeners.environmentPrepared(environment);
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
//是否添加转换服务,后面在必要时转换环境
if (this.addConversionService) {
// 格式化转换器
ConversionService conversionService = ApplicationConversionService.getSharedInstance();
environment.setConversionService((ConfigurableConversionService) conversionService);
}
//配置属性源,包括默认配置和命令行配置,默认配置使用SpringApplication的setDefaultProperties(Map<String, Object> defaultProperties)方法设置
configurePropertySources(environment, args);
//设置配置文件
configureProfiles(environment, args);
}
2.1、配置属性源
org.springframework.boot.SpringApplication#configurePropertySources
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) {
String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
if (sources.contains(name)) {
//通过commandLineArgs得到配置
PropertySource<?> source = sources.get(name);
//new一个CompositePropertySource,是PropertySource的子类,所以构造方法里传一个name知道啥意思了吧,前面介绍过PropertySource了
//该类中有一个Set<PropertySource<?>>
CompositePropertySource composite = new CompositePropertySource(name);
composite.addPropertySource(
new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args));
//用composite替换原 source中name为commandLineArgs的配置
composite.addPropertySource(source);
sources.replace(name, composite);
}
else {
//不存在就new一个SimpleCommandLinePropertySource实例添加
sources.addFirst(new SimpleCommandLinePropertySource(args));
}
}
}
2.2、配置配置文件
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
if (this.addConversionService) {
ConversionService conversionService = ApplicationConversionService.getSharedInstance();
environment.setConversionService((ConfigurableConversionService) conversionService);
}
configurePropertySources(environment, args);
// 开始分析
configureProfiles(environment, args);
}
protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
environment.getActiveProfiles(); // ensure they are initialized
// But these ones should go first (last wins in a property key clash)
Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
//设置ActiveProfiles
environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}
可以看到得到的配置文件就是配置项spring.profiles.active配置的。会判断this.activeProfiles是否为空,如果你是通过context.getEnvironment().setActiveProfiles(profiles)方法设置的,那就直接返回了。
org.springframework.core.env.AbstractEnvironment#getDefaultProfiles
public String[] getDefaultProfiles() {
return StringUtils.toStringArray(doGetDefaultProfiles());
}
protected Set<String> doGetDefaultProfiles() {
synchronized (this.defaultProfiles) {
if (this.defaultProfiles.equals(getReservedDefaultProfiles())) {
String profiles = getProperty(DEFAULT_PROFILES_PROPERTY_NAME);
if (StringUtils.hasText(profiles)) {
setDefaultProfiles(StringUtils.commaDelimitedListToStringArray(
StringUtils.trimAllWhitespace(profiles)));
}
}
return this.defaultProfiles;
}
}
public void setDefaultProfiles(String... profiles) {
Assert.notNull(profiles, "Profile array must not be null");
synchronized (this.defaultProfiles) {
this.defaultProfiles.clear();
for (String profile : profiles) {
validateProfile(profile);
this.defaultProfiles.add(profile);
}
}
}
3、发布ApplicationEnvironmentPreparedEvent事件
org.springframework.boot.SpringApplication#prepareEnvironment
listeners.environmentPrepared(environment);
org.springframework.boot.SpringApplicationRunListeners#environmentPrepared
public void environmentPrepared(ConfigurableEnvironment environment) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.environmentPrepared(environment);
}
}
事件的具体发布过程: 由于listener具体是实现类:EventPublishingRunListener
public void environmentPrepared(ConfigurableEnvironment environment) {
this.initialMulticaster
.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
}
this.initialMulticaster.multicastEvent 的具体实现前面有讲解:
@Override
public void multicastEvent(ApplicationEvent event) {
multicastEvent(event, resolveDefaultEventType(event));
}
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
Executor executor = getTaskExecutor();
// 重点是由于getApplicationListeners 方法,通过获取具体对应的简体类, 后面进行
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
调用的堆栈信息:
at org.springframework.cloud.bootstrap.BootstrapApplicationListener.onApplicationEvent(BootstrapApplicationListener.java:93)
at org.springframework.cloud.bootstrap.BootstrapApplicationListener.onApplicationEvent(BootstrapApplicationListener.java:71)
at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172)
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:127)
at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:75)
at org.springframework.boot.SpringApplicationRunListeners.environmentPrepared(SpringApplicationRunListeners.java:53)
at org.springframework.boot.SpringApplication.prepareEnvironment(SpringApplication.java:340)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:304)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1213)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1202)
at cn.wingsing.RegisterApplication.main(RegisterApplication.java:14)
调用监听实现类:
BootstrapApplicationListener
org.springframework.cloud.bootstrap.BootstrapApplicationListener#onApplicationEvent
org.springframework.cloud.bootstrap.BootstrapApplicationListener#bootstrapServiceContext
private ConfigurableApplicationContext bootstrapServiceContext(
ConfigurableEnvironment environment, final SpringApplication application,
String configName) {
StandardEnvironment bootstrapEnvironment = new StandardEnvironment();
MutablePropertySources bootstrapProperties = bootstrapEnvironment
.getPropertySources();
for (PropertySource<?> source : bootstrapProperties) {
bootstrapProperties.remove(source.getName());
}
String configLocation = environment
.resolvePlaceholders("${spring.cloud.bootstrap.location:}");
Map<String, Object> bootstrapMap = new HashMap<>();
bootstrapMap.put("spring.config.name", configName);
// if an app (or test) uses spring.main.web-application-type=reactive, bootstrap
// will fail
// force the environment to use none, because if though it is set below in the
// builder
// the environment overrides it
bootstrapMap.put("spring.main.web-application-type", "none");
if (StringUtils.hasText(configLocation)) {
bootstrapMap.put("spring.config.location", configLocation);
}
bootstrapProperties.addFirst(
new MapPropertySource(BOOTSTRAP_PROPERTY_SOURCE_NAME, bootstrapMap));
for (PropertySource<?> source : environment.getPropertySources()) {
if (source instanceof StubPropertySource) {
continue;
}
bootstrapProperties.addLast(source);
}
// TODO: is it possible or sensible to share a ResourceLoader?
SpringApplicationBuilder builder = new SpringApplicationBuilder()
.profiles(environment.getActiveProfiles()).bannerMode(Mode.OFF)
.environment(bootstrapEnvironment)
// Don't use the default properties in this builder
.registerShutdownHook(false).logStartupInfo(false)
.web(WebApplicationType.NONE);
final SpringApplication builderApplication = builder.application();
if (builderApplication.getMainApplicationClass() == null) {
// gh_425:
// SpringApplication cannot deduce the MainApplicationClass here
// if it is booted from SpringBootServletInitializer due to the
// absense of the "main" method in stackTraces.
// But luckily this method's second parameter "application" here
// carries the real MainApplicationClass which has been explicitly
// set by SpringBootServletInitializer itself already.
builder.main(application.getMainApplicationClass());
}
if (environment.getPropertySources().contains("refreshArgs")) {
// If we are doing a context refresh, really we only want to refresh the
// Environment, and there are some toxic listeners (like the
// LoggingApplicationListener) that affect global static state, so we need a
// way to switch those off.
builderApplication
.setListeners(filterListeners(builderApplication.getListeners()));
}
builder.sources(BootstrapImportSelectorConfiguration.class);
final ConfigurableApplicationContext context = builder.run();
// gh-214 using spring.application.name=bootstrap to set the context id via
// `ContextIdApplicationContextInitializer` prevents apps from getting the actual
// spring.application.name
// during the bootstrap phase.
context.setId("bootstrap");
// Make the bootstrap context a parent of the app context
addAncestorInitializer(application, context);
// It only has properties in it now that we don't want in the parent so remove
// it (and it will be added back later)
bootstrapProperties.remove(BOOTSTRAP_PROPERTY_SOURCE_NAME);
mergeDefaultProperties(environment.getPropertySources(), bootstrapProperties);
return context;
}
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
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
listeners.environmentPrepared(environment);
// 开始分析
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
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
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
listeners.environmentPrepared(environment);
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
// 开始分析
ConfigurationPropertySources.attach(environment);
return environment;
}
public static void attach(Environment environment) {
Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
MutablePropertySources sources = ((ConfigurableEnvironment) environment).getPropertySources();
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) {
sources.addFirst(new ConfigurationPropertySourcesPropertySource(ATTACHED_PROPERTY_SOURCE_NAME,
new SpringConfigurationPropertySources(sources)));
}
}
总结:
整个prepareEnvironment()方法走完以后得到的Environment如下:
StandardServletEnvironment {
activeProfiles=[],
defaultProfiles=[default],
propertySources=[
StubPropertySource {name='servletConfigInitParams'},
StubPropertySource {name='servletContextInitParams'},
PropertiesPropertySource {name='systemProperties'},
SystemEnvironmentPropertySource {name='systemEnvironment'}
]
}
其中ApplicationEnvironmentPreparedEvent事件的发布主要做的就是加载配置文件,初始化日志系统。
六、SpringApplication.run调用两次
"main@1" prio=5 tid=0x1 nid=NA runnable
java.lang.Thread.State: RUNNABLE
at org.springframework.boot.SpringApplication.run(SpringApplication.java:300)
at org.springframework.boot.builder.SpringApplicationBuilder.run(SpringApplicationBuilder.java:139)
- locked <0x1ba2> (a java.util.concurrent.atomic.AtomicBoolean)
at org.springframework.cloud.bootstrap.BootstrapApplicationListener.bootstrapServiceContext(BootstrapApplicationListener.java:203)
at org.springframework.cloud.bootstrap.BootstrapApplicationListener.onApplicationEvent(BootstrapApplicationListener.java:114)
at org.springframework.cloud.bootstrap.BootstrapApplicationListener.onApplicationEvent(BootstrapApplicationListener.java:71)
at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172)
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:127)
at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:75)
at org.springframework.boot.SpringApplicationRunListeners.environmentPrepared(SpringApplicationRunListeners.java:53)
at org.springframework.boot.SpringApplication.prepareEnvironment(SpringApplication.java:340)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:304)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1213)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1202)
at cn.wingsing.RegisterApplication.main(RegisterApplication.java:14)
以上调用的堆栈信息,调用两次的原因:
第一次调用是服务启动的入库main方法的正常调用;
第二次调用的原因:
org.springframework.boot.SpringApplication#prepareEnvironment
中listeners.environmentPrepared(environment);
实现的EventPublishingRunListener事件监听的environmentPrepared方法,通过 this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment)); 广播ApplicationEnvironmentPreparedEvent。
BootstrapApplicationListener.onApplicationEvent的方法监听
org.springframework.cloud.bootstrap.BootstrapApplicationListener#bootstrapServiceContext中
// 配置启动的
SpringApplicationBuilder builder = new SpringApplicationBuilder()
.profiles(environment.getActiveProfiles()).bannerMode(Mode.OFF)
.environment(bootstrapEnvironment)
// Don’t use the default properties in this builder
.registerShutdownHook(false).logStartupInfo(false)
.web(WebApplicationType.NONE); // 注意配置NONE
final ConfigurableApplicationContext context = builder.run(); 调用了run()方法.
前面讲的过 SpringApplicationBuilder 是SpringApplication的包装类,其实就是SpringApplication.run调用