回顾前章
说明:本章在之前章节《SringBoot到Spring源码分析之Environment环境装配》的基础上进行继续源码分析。但是本章内容不需要强依赖前面文章,可以单独阅读。
这里先简单总结前面环境对象装配的主要的过程和内容如下:
1、根据SpringApplication对象webApplicationType属性实例化环境对象StandardServletEnvironment。
2、通过环境对象构造器的方式初始化了所有系统环境变量、JDK系统变量、Servlet上下文参数、Servlet配置参数等信息。这些初始化的数据通过Map数据KV结构存根对象(StubPropertySource),按固定的顺序保存到CopyOnWriteArrayList集合中,保存的数据结构类似List<Map<String, Object>>,其中里面的Map的key为环境类型,value为对应环境的信息封装对象。
3、实例化ApplicationConversionService对象,通过构造器方式默认初始化了130多个类型转换器以及格式化组件,这里思考下为什么要在这一步初始化类型转换器和格式化组件。
4、通过单独函数的方式解析main方法参数,如果有的话。
5、尝试解析Active Profiles配置,实际这里不会解析到。
6、将MutablePropertySources封装为ConfigurationPropertySourcesPropertySource。
7、发布环境对象准备完成事件,这一步很关键,也是本章继续分析的重点,因为整个application.properties/yml配置文件就是通过事件回调的方式去解析的。
8、绑定spring.main开头的配置到SpringApplication对象对应的属性中。
9、根据根据第6步解析的spring.main.web-application-type配置值进行判断是否需要重新变换环境对象。如果spring.main.web-application-type有配置的话。
10、解除前面的MutablePropertySources封装
11、整个环境对象装配完成。
下面是前面章节涉及的顶层调用代码:
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
//1、根据SpringApplication对象webApplicationType属性实例化环境对象StandardServletEnvironment。
//2、通过环境对象构造器的方式初始化了所有系统环境变量、JDK系统变量、Servlet上下文参数、Servlet配置参数等信息。这些初始化的数据通过Map数据KV结构存根对象(StubPropertySource),按固定的顺序保存到CopyOnWriteArrayList集合中,保存的数据结构类似List<Map<String, Object>>,其中里面的Map的key为环境类型,value为对应环境的信息封装对象。
ConfigurableEnvironment environment = getOrCreateEnvironment();
//3、实例化ApplicationConversionService对象,通过构造器方式默认初始化了130多个类型转换器已经格式化组件。
//4、通过单独函数的方式解析main方法参数,如果有的话。
//5、尝试解析Active Profiles配置,实际这里不会解析到。
configureEnvironment(environment, applicationArguments.getSourceArgs());
//6、将MutablePropertySources封装为ConfigurationPropertySourcesPropertySource
ConfigurationPropertySources.attach(environment);
//7、发布环境对象准备完成事件,这一步很关键,也是本章继续分析的重点,因为整个application.properties或application.yml配置文件就是通过事件回调的方式去解析的。
listeners.environmentPrepared(environment);
//8、绑定spring.main开头的配置到SpringApplication对象对应的属性中。
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
//9、根据根据第6步解析的spring.main.web-application-type配置值进行判断是否需要重新变换环境对象。如果spring.main.web-application-type有配置的话。
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
//10、解除前面的MutablePropertySources封装
ConfigurationPropertySources.attach(environment);
//11、整个环境对象装配完成。
return environment;
}
再谈事件监听器
我们本章重点分析第7步骤,其它步骤配合前面的《SringBoot到Spring源码分析之Environment环境装配》章节有详细的关键代码截图。第7步骤主要是获取环境准备完成事件回调通知。获取事件监听器原理是读取classpath下所有的META-INF/spring.factories文件,根据事件监听器接口信息匹配逻辑如下:
1、如果实现SmartApplicationListener接口:通过接口全路径名称 + 调用SmartApplicationListener#supportsEventType子类实现方法 + 调用SmartApplicationListener#supportsSourceType子类实现方法结果均为true。
2、如果没有实现SmartApplicationListener接口:直接判断是否实现了传入的接口来匹配,也就是说判断是否是ApplicationListener接口的子类,例如这里的ApplicationListener接口。如果直接实现ApplicationListener接口而没有实现SmartApplicationListener接口的方式,在实现方法中需要自己去判断事件类型来处理相应的事件。例如像下面这样:
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationStartingEvent) {
onApplicationStartingEvent((ApplicationStartingEvent) event);
}
else if (event instanceof ApplicationEnvironmentPreparedEvent) {
onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
}
else if (event instanceof ApplicationPreparedEvent) {
onApplicationPreparedEvent((ApplicationPreparedEvent) event);
}
else if (event instanceof ContextClosedEvent
&& ((ContextClosedEvent) event).getApplicationContext().getParent() == null) {
onContextClosedEvent();
}
else if (event instanceof ApplicationFailedEvent) {
onApplicationFailedEvent();
}
}
ApplicationEnvironmentPreparedEvent事件监听器
下面是我们通过断点看到找到Springboot web起步依赖默认的环境准备完成事件监听器如下:
我们通过断点看到的信息是已经通过Order接口排好序之后的,执行过程也是根据这个顺序来的。下面简单介绍下这默认7个事件监听器:
ConfigFileApplicationListener: 解析application.properties/yml以及application-profile.properties/yml配置文件核心事件监听器,也是本章的重点分析对象。
AnsiOutputApplicationListener: 根据spring.output.ansi.enabled配置值配置日志打印色彩模式。
LoggingApplicationListener: 解析application.properties/yml配置文件logging开头的配置,并将配置信息初始化日志到系统。
BackgroundPreinitializer:这个监听器不处理当前的事件。
ClasspathLoggingApplicationListener:纯日志打印,启动过程中控制台debug级别的日志关键字“Application started with classpath”相关的信息就是这个监听器打印的。
Delegat