SpringBoot加载配置文件方法调用顺序图:
源码:
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
// 添加属性源
addPropertySources(environment, application.getResourceLoader());
// 配置忽略的Bean信息
configureIgnoreBeanInfo(environment);
// 绑定到SpringApplication
bindToSpringApplication(environment, application);
}
一、addPropertySources方法
protected void addPropertySources(ConfigurableEnvironment environment,
ResourceLoader resourceLoader) {
RandomValuePropertySource.addToEnvironment(environment);
// 加载配置文件
new Loader(environment, resourceLoader).load();
}
这里resourceLoader为空则默认new DefaultResourceLoader
1.1 load方法
public void load() {
this.propertiesLoader = new PropertySourcesLoader();
this.activatedProfiles = false;
this.profiles = Collections.asLifoQueue(new LinkedList<Profile>());
this.processedProfiles = new LinkedList<Profile>();
// Pre-existing active profiles set via Environment.setActiveProfiles()
// are additional profiles and config files are allowed to add more if
// they want to, so don't call addActiveProfiles() here.
// 从环境中获取spring.profiles.active和spring.profiles.include这两个配置
// debug时这里为空
Set<Profile> initialActiveProfiles = initializeActiveProfiles();
// 获取环境中还未处理的profiles
// debug时这里没有
this.profiles.addAll(getUnprocessedActiveProfiles(initialActiveProfiles));
if (this.profiles.isEmpty()) {
// 从环境中拿到默认的profiles
for (String defaultProfileName : this.environment.getDefaultProfiles()) {
Profile defaultProfile = new Profile(defaultProfileName, true);
if (!this.profiles.contains(defaultProfile)) {
this.profiles.add(defaultProfile);
}
}
}
// The default profile for these purposes is represented as null. We add it
// last so that it is first out of the queue (active profiles will then
// override any settings in the defaults when the list is reversed later).
this.profiles.add(null);
while (!this.profiles.isEmpty()) {
Profile profile = this.profiles.poll();
for (String location : getSearchLocations()) {
if (!location.endsWith("/")) {
// location is a filename already, so don't search for more
// filenames
load(location, null, profile);
}
else {
for (String name : getSearchNames()) {
// 从路径 file:./config/ file:./ classpath:/config/ classpath:/
加载名字叫application,后缀名叫:properties xml yml yaml 的文件,加载的属性源文件存放在PropertySourcesLoader中,然后添加到配置属性中去
load(location, name, profile);
}
}
}
this.processedProfiles.add(profile);
}
addConfigurationProperties(this.propertiesLoader.getPropertySources());
}
做了6件事:
1. 从环境中获取spring.profiles.active和spring.profiles.include这两个配置,debug时这里为空
2.从环境中拿到默认的profiles
3. getSearchLocations()方法,如果环境中有指定spring.config.location配置文件的路径,则把路径也加入。默认搜索的路径有4个:file:./config/ file:./ classpath:/config/ classpath:/
4.getSearchNames()方法,如果环境中指定了spring.config.name配置文件的名字,则以该名字搜索。否则以默认配置文件名字application搜索
5.如果路径以/结尾,直接加载配置文件。如果不是以/结尾,分别在3中的四个路径中搜索名字为application,后缀名为properties、xml、yml、yaml的配置文件。
6.加载配置文件。先加载application.yml,然后再加载application-dev.yml
1.1.1调用load(location,name,profile)方法加载配置文件
源码:
private void load(String location, String name, Profile profile) {
String group = "profile=" + (profile == null ? "" : profile);
if (!StringUtils.hasText(name)) {
// Try to load directly from the location
loadIntoGroup(group, location, profile);
}
else {
// Search for a file with the given name
for (String ext : this.propertiesLoader.getAllFileExtensions()) {
//debug的时候没进去
if (profile != null) {
// Try the profile-specific file
loadIntoGroup(group, location + name + "-" + profile + "." + ext,
null);
for (Profile processedProfile : this.processedProfiles) {
if (processedProfile != null) {
loadIntoGroup(group, location + name + "-"
+ processedProfile + "." + ext, profile);
}
}
// Sometimes people put "spring.profiles: dev" in
// application-dev.yml (gh-340). Arguably we should try and error
// out on that, but we can be kind and load it anyway.
loadIntoGroup(group, location + name + "-" + profile + "." + ext,
profile);
}
// Also try the profile-specific section (if any) of the normal file
// debug的时候 第一次加载的是application.yml走的是这个loadIntoGroup
loadIntoGroup(group, location + name + "." + ext, profile);
}
}
}
其中真正加载配置文件是调用的loadIntoGroup方法
1.1.1.1loadIntoGroup方法
private PropertySource<?> doLoadIntoGroup(String identifier, String location,
Profile profile) throws IOException {
// 加载到路径为classpath:/application.yml的配置文件,得到资源
Resource resource = this.resourceLoader.getResource(location);
PropertySource<?> propertySource = null;
StringBuilder msg = new StringBuilder();
if (resource != null && resource.exists()) {
String name = "applicationConfig: [" + location + "]";
String group = "applicationConfig: [" + identifier + "]";
// 调用PropertySourcesLoader类的load方法
propertySource = this.propertiesLoader.load(resource, group, name,
(profile == null ? null : profile.getName()));
if (propertySource != null) {
msg.append("Loaded ");
//
handleProfileProperties(propertySource);
}
else {
msg.append("Skipped (empty) ");
}
}
else {
msg.append("Skipped ");
}
msg.append("config file ");
msg.append(getResourceDescription(location, resource));
if (profile != null) {
msg.append(" for profile ").append(profile);
}
if (resource == null || !resource.exists()) {
msg.append(" resource not found");
this.logger.trace(msg);
}
else {
this.logger.debug(msg);
}
return propertySource;
}
测试时,项目目录为:
1.1.1.1.1 会在走到该方法中的Resource resource = this.resourceLoader.getResource(location)方法时加载到
路径为classpath:/application.yml配置文件
1.1.1.1.2 this.propertiesLoader.load方法
调用PropertySourcesLoader类的load方法
public PropertySource<?> load(Resource resource, String group, String name,
String profile) throws IOException {
if (isFile(resource)) {
String sourceName = generatePropertySourceName(name, profile);
// 里面的this.loaders有两个:
//PropertiesPropertySourceLoader和YamlPropertySourceLoader
for (PropertySourceLoader loader : this.loaders) {
//实际上debug的时候PropertiesPropertySourceLoader这个if没通过
if (canLoadFileExtension(loader, resource)) {
// 调用YamlPropertySourceLoader的load方法
// 它会加载该application.yml文件,把里面的配置以MapPropertySource的形式返回
PropertySource<?> specific = loader.load(sourceName, resource,
profile);
//
addPropertySource(group, specific, profile);
return specific;
}
}
}
return null;
}
里面的this.loaders有两个:
PropertiesPropertySourceLoader和YamlPropertySourceLoader
实际上debug的时候PropertiesPropertySourceLoader这个if(canLoadFileExtension)没通过
1.1.1.1.2.1 调用YamlPropertySourceLoader的load方法
public PropertySource<?> load(String name, Resource resource, String profile)
throws IOException {
if (ClassUtils.isPresent("org.yaml.snakeyaml.Yaml", null)) {
Processor processor = new Processor(resource, profile);
Map<String, Object> source = processor.process();
if (!source.isEmpty()) {
return new MapPropertySource(name, source);
}
}
return null;
}
它会加载该application.yml文件,把里面的配置以MapPropertySource的形式返回
比如里面的配置是:
返回是:
1.1.1.1.2.2 调用PropertySourcesLoader类的addPropertySource方法
源码:
private void addPropertySource(String basename, PropertySource<?> source,
String profile) {
if (source == null) {
return;
}
if (basename == null) {
this.propertySources.addLast(source);
return;
}
EnumerableCompositePropertySource group = getGeneric(basename);
group.add(source);
logger.trace("Adding PropertySource: " + source + " in group: " + basename);
if (this.propertySources.contains(group.getName())) {
this.propertySources.replace(group.getName(), group);
}
else {
this.propertySources.addFirst(group);
}
}
1.1.1.1.3 handleProfileProperties方法
调用ConfigFileApplicationListener类的handleProfileProperties方法
源码:
private void handleProfileProperties(PropertySource<?> propertySource) {
// 这里能够解析出当前active的文件名是以dev结尾
SpringProfiles springProfiles = bindSpringProfiles(propertySource);
//设置当前环境active配置文件为dev文件
maybeActivateProfiles(springProfiles.getActiveProfiles());
// profiles为空没有进到处理逻辑
addProfiles(springProfiles.getIncludeProfiles());
}