在Spring Boot源码分析-启动过程中我们进行了启动源码的分析,大致了解了整个Spring Boot
的启动过程,具体细节这里不再赘述,感兴趣的同学可以自行阅读。今天让我们继续阅读源码,了解配置文件加载原理。
基于
Spring Boot 2.1.0.RELEASE
在开始阅读源码之前,首先准备三个问题。
- 什么时候开始加载配置文件?
- 如何读取相关配置文件内容?
- 如何区分不同环境的配置?
下面用Spring
代替Spring Boot
接下来进入主题,首先关注第一个问题。
一、什么时候开始加载配置文件?
从Spring Boot源码分析-启动过程中我们可以得知,Spring
在启动的过程中发布了ApplicationEnvironmentPreparedEvent
事件,ConfigFileApplicationListener
监听到这个消息的时候,开始实例化并调用(META-INF/spring.factories
中定义)EnvironmentPostProcessor
的postProcessEnvironment
方法。而ConfigFileApplicationListener
本身也实现了EnvironmentPostProcessor
接口,且将自身加入到EnvironmentPostProcessor
集合中,故也会调用自身的方法。
跟踪ConfigFileApplicationListener
的postProcessEnvironment
方法源码
public void postProcessEnvironmen(ConfigurableEnvironment environment,
SpringApplication application) {
addPropertySources(environment,application.getResourceLoader());
}
继续跟踪addPropertySources
方法
/**
* Add config file property sources to the specified environment.
* @param environment the environment to add source to
* @param resourceLoader the resource loader
* @see #addPostProcessors(ConfigurableApplicationContext)
*/
protected void addPropertySources(ConfigurableEnvironmentenvironment,
ResourceLoader resourceLoader) {
RandomValuePropertySource.addToEnvironmen(environment);
new Loader(environment, resourceLoader).load();
}
从注释中我们可以看出,这个方法是将配置文件内容添加到指定的Environment
中。到此为止,我们已经明白了Spring
是在发布ApplicationEnvironmentPreparedEvent
事件之后,才开始加载配置文件的。接下来开始关注第二个问题。
二、如何读取相关配置文件内容?
继续跟踪Loader
源码,Loader
是ConfigFileApplicationListener
的一个内部类,用来读取配置文件并配置相关环境。
首先跟踪Loader
构造方法(注意load
存在多个方法重载)
Loader(ConfigurableEnvironment environmentResourceLoader resourceLoader) {
this.environment = environment;
this.placeholdersResolver = nePropertySourcesPlaceholdersResolver(
this.environment);
this.resourceLoader = (resourceLoader != null) resourceLoader
: new DefaultResourceLoader();
// 实例化配置文件读取工具
this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(
PropertySourceLoader.class, getClass.getClassLoader());
}
SpringFactoriesLoader.loadFactories
获取META-INF/spring.factories
中预定义的类
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader
从类名中可以看出这两个类主要是用来读取.properties
和.yml
文件
继续跟踪load
方法
public void load() {
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,
addToLoad(MutablePropertySources::addLastfalse));
this.processedProfiles.add(profile);
}
resetEnvironmentProfiles(this.processedProfiles);
load(null, this::getNegativeProfileFilter,
addToLoad(MutablePropertySources::addFirst, true));
addLoadedPropertySources();
}
继续跟踪initializeProfiles
方法
/**
* Initialize profile information from both the {@link Environment} active
* profiles and any {@code spring.profiles.active{@code spring.profiles.include}
* properties that are already set.
*/
private void initializeProfiles() {
// The default profile for these purposes irepresented as null. We add it
// first so that it is processed first and halowest priority.
this.profiles.add(null);
Set<Profile> activatedViaProperty = getProfilesActivatedViaProperty();
this.profiles.addAll(getOtherActiveProfil(activatedViaProperty));
// Any pre-existing active profiles set viproperty sources (e.g.
// System properties) take precedence over thosadded in config files.
addActiveProfiles(activatedViaProperty);
if (this.profiles.size() == 1) { // only has nulprofile
for (String defaultProfileName this.environment.getDefaultProfiles()) {
Profile defaultProfile = new Profi(defaultProfileName, true);
this.profiles.add(defaultProfile);
}
}
}
从注释中我们可以了解到这个方法用来初始化profile
。继续往下看Spring
如何初始化profile
。接着跟踪getProfilesActivatedViaProperty
方法。
private Set<Profile> getProfilesActivatedViaProperty {
if (!this.environment.containsProper(ACTIVE_PROFILES_PROPERTY)
&& !this.environment.containsProper(INCLUDE_PROFILES_PROPERTY)) {
return Collections.emptySet();
}
Binder binder = Binder.get(this.environment);
Set<Profile> activeProfiles = new LinkedHashSet();
activeProfiles.addAll(getProfiles(binderINCLUDE_PROFILES_PROPERTY));
activeProfiles.addAll(getProfiles(binderACTIVE_PROFILES_PROPERTY));
return activeProfiles;
}
Environment
目前没有读取配置文件,故这里返