springboot application配置文件读取顺序

默认配置文件

springboot默认会自动加载配置文件application.properties或application.yml文件。在springboot启动时,依次会从以下目录进行查找配置文件:

1、classpath

​ 首先从classpath目录下查找配置文件,然后从 classpath:/config目录下查找配置文件

2、当前目录

​ 首先从当前目录下查找,然后从./config目录下查找

启动时会按照上面的顺序进行加载配置文件,如果存在多个,后面的会覆盖前面的配置项值。读取的配置项参数会封装成一个PropertySources 对象存储到spring的environment中。environment在springboot中被暴露成一个bean,可以直接在需要地方注入使用。

配置文件名:

默认情况下配置文件的名称为application,如果不想使用该文件名,可以通过spring.config.name参数来指定具体的文件名,一般也没人会改吧。同样的你如果不想从上面默认位置加载配置文件,也可以通过spring.config.location参数来指定配置文件位置,多个配置文件用英文逗号相隔。

如下配置:

java -jar boot.jar -Dspring.config.location=optional:classpath:/default.properties,optional:classpath:/extend.properties

这里的optional意思可选的意思,当文件不存在时不会报错。spring.config.location可以指定配置时一个目录,如果是目录要以/结尾。

spring.config.name和spring.config.location两个配置项都是在读取配置文件前来确定文件名和文件位置,因此需要配置在启动参数里,或者系统环境变量。environment默认会对其这些配置。

额外配置文件

如果有多个配置文件,只是根据spring.config.name读取一个配置文件无法满足要求,可以使用import来引入额外的配置文件

spring.config.import=optional:classpath:/ext/extend.properties

这个配置可以配置在application配置文件中,多个用英文逗号隔开。

profile特定配置文件

springboot在加载完默认配置文件和可能的import文件后,还会根据当前配置的profile值进行读取配置文件,读取文件名application-{profile}。文件位置还是和上面的一致。可以通过spring.profiles.active来指定profile。可以指定多个用英文逗号隔开,默认值是default。也就是在不配置的情况下,会尝试读取配置文件application-default。

如果这里指定spring.profiles.active=dev,则会读取application-dev配置文件。spring.profiles.active的值即可以通过启动参数指定,也可以通过在application配置文件中指定。

值得主要的是对应额外import的文件,也会尝试按profile进行读取。如上面配置的import文件/ext/extend.properties,则会尝试读取/ext/extend-dev.properties

源码分析

1、SpringApplication初始化

SpringApplication类有两个属性

private List<ApplicationContextInitializer<?>> initializers;
private List<ApplicationListener<?>> listeners;

在SpringApplication的构造函数会通过SpringFactoriesLoader根据配置进行加载

setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

listeners有8个,其中一个是EnvironmentPostProcessorApplicationListener。

然后调用SpringApplication.run()方法。

会通过SpringFactoriesLoader加载SpringApplicationRunListener

java 复制代码
private SpringApplicationRunListeners getRunListeners(String[] args) {
	Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
	return new SpringApplicationRunListeners(logger,
			getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args),
			this.applicationStartup);
}

获取的runListener实例是EventPublishingRunListener类型。

EventPublishingRunListener创建的时候会调用其有参构造函数

java 复制代码
public EventPublishingRunListener(SpringApplication application, String[] args) {
	this.application = application;
	this.args = args;
	this.initialMulticaster = new SimpleApplicationEventMulticaster();
	for (ApplicationListener<?> listener : application.getListeners()) {
		this.initialMulticaster.addApplicationListener(listener);
	}
}

这里就将最开始SpringApplication初始化的8哥listeners传给了EventMulticaster。看到这里就知道会使用EventMulticaster来发布事件,不知道spring事件通知机制的可以看下前面的一篇文章。

spring event事件通知机制

2、初始化environment,发布事件

再往下开始初始化environment,首先创建一个environment。这时候environment里只有系统变量和启动参数变量等基本信息。

SpringApplication#prepareEnvironment

java 复制代码
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
      DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
   // Create and configure the environment
   ConfigurableEnvironment environment = getOrCreateEnvironment();
   configureEnvironment(environment, applicationArguments.getSourceArgs());
   ConfigurationPropertySources.attach(environment);
  //发布事件
   listeners.environmentPrepared(bootstrapContext, environment);
   DefaultPropertiesPropertySource.moveToEnd(environment);
   Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
         "Environment prefix cannot be set via properties.");
   bindToSpringApplication(environment);
   if (!this.isCustomEnvironment) {
      environment = convertEnvironment(environment);
   }
   ConfigurationPropertySources.attach(environment);
   return environment;
}

environment创建完成后会通过listeners.environmentPrepared方法来发布环境就绪事件,这里listners只有一个EventPublishingRunListener类型实例,其environmentPrepared方法会通过EventMulticaster来发布ApplicationEnvironmentPreparedEvent事件,事件发布后会上面初始化的8个 EventMulticaster的listeners其中之一EnvironmentPostProcessorApplicationListener会接收到事件,会在其onApplicationEnvironmentPreparedEvent()方法处理事件,处理事件是会通过SpringFactoriesLoader来加载EnvironmentPostProcessor类型的processor,逐个调用其postProcessEnvironment()方法,其中一个是ConfigDataEnvironmentPostProcessor,这个是主要来处理配置文件的processor。

3、配置文件读取

ConfigDataEnvironmentPostProcessor会使用ConfigDataEnvironment来处理配置文件读取。

来看下ConfigDataEnvironment的静态初始化块

java 复制代码
static final ConfigDataLocation[] DEFAULT_SEARCH_LOCATIONS;
static {
   List<ConfigDataLocation> locations = new ArrayList<>();
   locations.add(ConfigDataLocation.of("optional:classpath:/;optional:classpath:/config/"));
   locations.add(ConfigDataLocation.of("optional:file:./;optional:file:./config/;optional:file:./config/*/"));
   DEFAULT_SEARCH_LOCATIONS = locations.toArray(new ConfigDataLocation[0]);
}

DEFAULT_SEARCH_LOCATIONS是配置文件默认搜索位置,看到这里就能明白配置文件的读取顺序了吧。像前面提到的spring.config.location和spring.config.import配置在该类里都有对应的变量来去读取。

ConfigDataEnvironment创建时候会将envirmoent对象传进来,读取配置文件通过processAndApply()方法。

java 复制代码
void processAndApply() {
   ConfigDataImporter importer = new ConfigDataImporter(this.logFactory, this.notFoundAction, this.resolvers,
         this.loaders);
   registerBootstrapBinder(this.contributors, null, DENY_INACTIVE_BINDING);
   ConfigDataEnvironmentContributors contributors = processInitial(this.contributors, importer);
   ConfigDataActivationContext activationContext = createActivationContext(
         contributors.getBinder(null, BinderOption.FAIL_ON_BIND_TO_INACTIVE_SOURCE));
   contributors = processWithoutProfiles(contributors, importer, activationContext);
   activationContext = withProfiles(contributors, activationContext);
   contributors = processWithProfiles(contributors, importer, activationContext);
   applyToEnvironment(contributors, activationContext, importer.getLoadedLocations(),
         importer.getOptionalLocations());
}

这里看到先是通过processWithoutProfiles方法解析不带profiles的配置文件,然后通过processWithProfiles解析带profiles的配置文件,最后将解析到的配置文件设置到environment中。再往下的代码就不看了。

提以下最后配置文件的解析,这里又是通过SpringFactoriesLoader来加载PropertySourceLoader类型的配置文件解析类。这里配置有两个PropertiesPropertySourceLoader和YamlPropertySourceLoader。一个用来解析properties文件,一个用来解析YamlPropertySourceLoader配置文件。

相关推荐
monkey_meng5 分钟前
【Rust类型驱动开发 Type Driven Development】
开发语言·后端·rust
落落落sss13 分钟前
MQ集群
java·服务器·开发语言·后端·elasticsearch·adb·ruby
我救我自己13 分钟前
UE5运行时创建slate窗口
java·服务器·ue5
2401_8532757334 分钟前
ArrayList 源码分析
java·开发语言
爪哇学长38 分钟前
SQL 注入详解:原理、危害与防范措施
xml·java·数据库·sql·oracle
大鲤余43 分钟前
Rust,删除cargo安装的可执行文件
开发语言·后端·rust
她说彩礼65万1 小时前
Asp.NET Core Mvc中一个视图怎么设置多个强数据类型
后端·asp.net·mvc
MoFe11 小时前
【.net core】【sqlsugar】字符串拼接+内容去重
java·开发语言·.netcore
陈随易1 小时前
农村程序员-关于小孩教育的思考
前端·后端·程序员
_江南一点雨1 小时前
SpringBoot 3.3.5 试用CRaC,启动速度提升3到10倍
java·spring boot·后端