文章目录
回顾
上一篇 Spring boot源码深入学习(三) | SpringApplication实例化以及第一个监听器事件发布
学习了springboot中:
1.SpringApplication的实例化流程
2.springboot读取spring.factories文件,获取监听器,第一次执行发布事件流程等等
下面接着后面的分析学习:
【1】获取监听器
【2】准备环境
【3】控制台打印Banner
【4】创建容器,根据不同类型创建不同的容器
【5】实例化异常报告期实例,用于记录启动过程中的错误。
【6】准备容器,给刚刚创建的容器做一些初始化工作
【7】刷新容器,这一步至关重要。后续再做解析
【8】刷新容器后的一些操作,这里是空方法
本文着重分析学习【2】准备环境
prepareEnvironment源码分析
prepareEnvironment源码步骤分析:
1.取得当前环境或者创建一个环境:SERVLET,REACTIVE,NONE
2.配置环境
3.加载配置文件.通过ApplicationEnvironmentPreparedEvent事件发布,执行对应的监听器事件
4.绑定环境到SpringApplication
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// 1.取得当前环境或者创建一个环境:SERVLET,REACTIVE,NONE
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 2.配置环境
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 3.加载配置文件.通过ApplicationEnvironmentPreparedEvent事件发布,执行对应的监听器事件
listeners.environmentPrepared(environment);
// 4.绑定环境到SpringApplication
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader())
.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
下面分步分析
初始化环境getOrCreateEnvironment
private ConfigurableEnvironment getOrCreateEnvironment() {
// 当前有环境配置,则返回
if (this.environment != null) {
return this.environment;
}
// 根据类型初始化环境
switch (this.webApplicationType) {
// SERVLET环境
case SERVLET:
return new StandardServletEnvironment();
// REACTIVE环境
case REACTIVE:
return new StandardReactiveWebEnvironment();
// 默认环境none
default:
return new StandardEnvironment();
}
}
由代码可知webApplicationType分为SERVLET,REACTIVE,NONE三种环境,根据此类型初始化环境
NONE:不需要再web容器的环境下运行,普通项目
SERVLET:基于servlet的web项目
REACTIVE:spring5版本开始的新特性
Environment接口提供了4种实现方式:StandardEnvironment(NONE):普通程序StandardServletEnvironment(SERVLET):Web程序MockEnvironment(测试):测试程序的环境StandardReactiveWebEnvironment(REACTIVE):响应式web环境
解析:
这里就是初始化环境,SERVLET环境返回的就是StandardServletEnvironment对象,关于配置和环境的操作都基于此类
首先看一下类图:
我们可以看到StandardServletEnvironment的父类StandardEnvironment以及父父类AbstractEnvironment类,子类的实例化肯定代表着父类也实例化,看一看抽象类AbstractEnvironment的构造器
可以看到这里采用的是模板模式,调用的是实例对象的自定义逻辑,添加了web相关servletConfigInitParams和servletContextInitParams:
super.customizePropertySources(propertySources)调用父类StandardEnvironment的customizePropertySources方法,添加系统变量systemProperties和系统环境变量systemEnvironment
总体概括:
在返回return new StandardServletEnvironment()对象的时候,会完成一系列初始化动作,
1.将运行机器的系统变量和环境变量,加入到其父类AbstractEnvironment定义的对象MutablePropertySources中的集合propertySourceList中
2.解析项目中的配置文件
3.执行系统环境初始化完成事件listeners.environmentPrepared(environment);
配置环境configureEnvironment
configureEnvironment(environment, applicationArguments.getSourceArgs());
protected void configureEnvironment(ConfigurableEnvironment environment,
String[] args) {
if (this.addConversionService) {
ConversionService conversionService = ApplicationConversionService
.getSharedInstance();
environment.setConversionService(
(ConfigurableConversionService) conversionService);
}
// 加载启动命令行配置属性
configurePropertySources(environment, args);
// 设置active属性
configureProfiles(environment, args);
}
configurePropertySources
1.若默认配置不为空,则环境中添加默认配置defaultProperties
2.添加命令行配置信息到环境
protected void configurePropertySources(ConfigurableEnvironment environment,
String[] args) {
// 获取环境中的配置信息
MutablePropertySources sources = environment.getPropertySources();
// 若默认配置不为空,则添加默认配置defaultProperties
if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
sources.addLast(
new MapPropertySource("defaultProperties", this.defaultProperties));
}
// 加载命令行的配置
if (this.addCommandLineProperties && args.length > 0) {
String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
if (sources.contains(name)) {
PropertySource<?> source = sources.get(name);
CompositePropertySource composite = new CompositePropertySource(name);
composite.addPropertySource(new SimpleCommandLinePropertySource(
"springApplicationCommandLineArgs", args));
composite.addPropertySource(source);
sources.replace(name, composite);
}
else {
sources.addFirst(new SimpleCommandLinePropertySource(args));
}
}
}
我们可以看到环境的配置信息是MutablePropertySources对象接收的,这里的信息就是之前new StandardServletEnvironment时,存入environment的
configureProfiles设置active属性
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()));
environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}
该方法将SpringBootApplication中指定的additionalProfiles文件加载到environment中,一般默认为空。
该变量的用法,在项目启动类中,需要显示创建SpringApplication实例,如下:
SpringApplication springApplication = new SpringApplication(MyApplication.class);
//设置profile变量
springApplication.setAdditionalProfiles("prd");
springApplication.run(MyApplication.class,args);
事件发布,加载配置文件listeners.environmentPrepared(environment)
通过ApplicationEnvironmentPreparedEvent事件发布,执行对应的监听器事件。监听器事件发布流程之前的文章已这里不作介绍。
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
this.application, this.args, environment));
}
@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));
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
这里可以看到环境准备过程有7个监听器,意味着要执行7个监听器事件,其中这一环节最重要的就是ConfigFileApplicationListener这个监听器,下面看一看它的执行流程:
private void onApplicationEnvironmentPreparedEvent(
ApplicationEnvironmentPreparedEvent event) {
// 加载spring.factories配置文件中的EnvironmentPostProcessor信息
// CloudFoundryVcapEnvironmentPostProcessor
// SpringApplicationJsonEnvironmentPostProcessor
// SystemEnvironmentPropertySourceEnvironmentPostProcessor
List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
// 添加监听器ConfigFileApplicationListener到postProcessors
postProcessors.add(this);
AnnotationAwareOrderComparator.sort(postProcessors);
// 执行EnvironmentPostProcessor和监听器事件逻辑
for (EnvironmentPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessEnvironment(event.getEnvironment(),
event.getSpringApplication());
}
}
跟进ConfigFileApplicationListener的处理逻辑:
protected void addPropertySources(ConfigurableEnvironment environment,
ResourceLoader resourceLoader) {
RandomValuePropertySource.addToEnvironment(environment);
new Loader(environment, resourceLoader).load();
}
这里可以看到一个new Loader的实例化,Loader就是加载配置文件的核心类,他是ConfigFileApplicationListener的内部类,下面看一下核心逻辑:
public void load() {
// LIFO队列
this.profiles = new LinkedList<>();
// 已经处理的文件
this.processedProfiles = new LinkedList<>();
// 已激活文件
this.activatedProfiles = false;
this.loaded = new LinkedHashMap<>();
// 初始化
initializeProfiles();
// 解析文件
while (!this.profiles.isEmpty()) {
Profile profile = this.profiles.poll();
// 添加已激活配置文件到环境
if (profile != null && !profile.isDefaultProfile()) {
addProfileToEnvironment(profile.getName());
}
// 获取默认配置文件路径,循环加载配置文件(不检查是否已经存在)
load(profile, this::getPositiveProfileFilter,
addToLoaded(MutablePropertySources::addLast, false));
this.processedProfiles.add(profile);
}
// 重置已经处理的文件
resetEnvironmentProfiles(this.processedProfiles);
// 对加载过的配置文件进行排序(排序就会检查是否存在)
load(null, this::getNegativeProfileFilter,
addToLoaded(MutablePropertySources::addFirst, true));
// 添加解析完的配置文件到environment中的集合对象MutablePropertySources中
addLoadedPropertySources();
}
初始化initializeProfiles()
1.取得当前环境中激活的配置文件,形如如spring.profiles.active指定的application.yaml文件。如没有则创建一个默认的配置文件,形如application-default.yml、application-default.properties
2.添加一个null的profile,主要用来加载没有指定profile的配置文件,比如:application.properties
因为 profiles 采用了 LIFO 队列,后进先出。所以会先加载profile为null的配置文件,也就是匹配application.properties、application.yml。
private void initializeProfiles() {
this.profiles.add(null);
// 取得已激活配置文件,如spring.profiles.active指定的application.yaml文件
Set<Profile> activatedViaProperty = getProfilesActivatedViaProperty();
this.profiles.addAll(getOtherActiveProfiles(activatedViaProperty));
addActiveProfiles(activatedViaProperty);
// 如没有已激活配置文件,则新建一个默认的配置文件
if (this.profiles.size() == 1) { // only has null profile
for (String defaultProfileName : this.environment.getDefaultProfiles()) {
Profile defaultProfile = new Profile(defaultProfileName, true);
this.profiles.add(defaultProfile);
}
}
}
// 取得已经指定的配置文件,如spring.profiles.active指定的yaml文件
private Set<Profile> getProfilesActivatedViaProperty() {
// 环境中没有spring.profiles.active和spring.profiles.include指定的文件,返回空
if (!this.environment.containsProperty(ACTIVE_PROFILES_PROPERTY)
&& !this.environment.containsProperty(INCLUDE_PROFILES_PROPERTY)) {
return Collections.emptySet();
}
// 返回active已经激活的配置文件
Binder binder = Binder.get(this.environment);
Set<Profile> activeProfiles = new LinkedHashSet<>();
activeProfiles.addAll(getProfiles(binder, INCLUDE_PROFILES_PROPERTY));
activeProfiles.addAll(getProfiles(binder, ACTIVE_PROFILES_PROPERTY));
return activeProfiles;
}
加载配置路径下的配置文件,如yaml或者properties文件
load(profile, this::getPositiveProfileFilter,addToLoaded(MutablePropertySources::addLast, false));
1.获取配置文件的路径
2.循环遍历路径,解析路径下的文件
private void load(Profile profile, DocumentFilterFactory filterFactory,
DocumentConsumer consumer) {
// 获取默认的配置文件路径
getSearchLocations().forEach((location) -> {
boolean isFolder = location.endsWith("/");
Set<String> names = isFolder ? getSearchNames() : NO_SEARCH_NAMES;
names.forEach(
// 根据yaml,properties的不同来解析配置文件
(name) -> load(location, name, profile, filterFactory, consumer));
});
}
将加载完的yaml或者properties文件放入StandardServletEnvironment中集合对象MutablePropertySources,可以看到有默认的application.properties文件最后加载。
总结
springboot中准备环境:
1.初始化环境:SERVLET,REACTIVE,NONE
2.配置环境
3.加载配置文件.通过ApplicationEnvironmentPreparedEvent事件发布,执行对应的监听器事件
4.绑定环境到SpringApplication
注意:加载完的配置文件存在MutablePropertySources中,优先级越高的在越前面。如图:
servletConfigInitParams > servletContextInitParams > systemProperties > application.yaml或者application.properties
参考学习博文:https://blog.csdn.net/woshilijiuyi/article/details/82720478