属性文件
SpringApplication从application.properties以下位置的文件加载属性并将它们添加到Spring Environment:
- 一个/config当前目录的子目录
- 当前目录当前目录
- 一个类路径/config包
- 类路径根类路径根
列表按优先级排序(在列表中较高位置定义的属性将覆盖在较低位置中定义的属性)
修改配置文件名称
场景:
如果不喜欢application.properties配置文件名,怎么修改?
解决:
可以通过指定spring.config.name环境属性切换到另一个文件名,我们将application.properties改成加载myproject.properties文件,会在[/config当前目录的子目录]、[当前目录当前目录]、[一个类路径/config包]、[类路径根类路径根]找myproject.properties文件。具体实现如下:
- 在代码中显示配置系统环境变量
@SpringBootApplication
public class ClientApplication {
public static void main(String[] args) {
System.setProperty("spring.config.name", "myproject");
SpringApplication sa = new SpringApplication(ClientApplication.class);
sa.run();
}
}
- 启动时候定义系统变量
$ java -jar myproject.jar --spring.config.name = myproject
修改配置文件默认路径
场景:
如果除了以上四个默认路径,想加载其他路径下的配置文件,怎么办?
1. 解决:
可以通过指定spring.config.location环境属性切换到另一个路径下
- 在代码中显示配置系统环境变量
@SpringBootApplication
public class ClientApplication {
public static void main(String[] args) {
System.setProperty("spring.config.name", "myproject");
System.setProperty("spring.config.location", "classpath:/myproject/");
SpringApplication sa = new SpringApplication(ClientApplication.class);
sa.run();
}
}
总结,会加载classpath:/myproject/myproject.properties文件,以上代码也可以写成如下格式:
@SpringBootApplication
public class ClientApplication {
public static void main(String[] args) {
System.setProperty("spring.config.location", "classpath:/myproject/myproject.properties");
SpringApplication sa = new SpringApplication(ClientApplication.class);
sa.run();
}
}
2 解决:
可以使用spring.config.additional-location
配置属性,具体配置参考上边spring.config.location
的配置
两者区别,如下:
System.setProperty("spring.config.additional-location", "classpath:/myproject/");
会在不仅在[classpath:/myproject/]找,而且还在[/config当前目录的子目录]、[当前目录当前目录]、[一个类路径/config包]、[类路径根类路径根]找。而spring.config.location
只在[classpath:/myproject/]路径下找
特定环境配置文件
1、可以使用以下命名约定来定义特定于配置文件的属性:application-{profile}.properties。
2、 特定于配置文件始终覆盖非特定的文件,如application-default.properties
会覆盖application.properties
3、如果指定了多个配置文件,可以通过spring.profiles.active
加载环境
3.1、在代码中显示增加SpringApplication .setAdditionalProfiles("dev");
3.2、运行Application.java文件启动,则增加参数System.setProperty("spring.profiles.active", "dev");
、
3.3、命令行直接运行jar文件,则使用java -jar -Dspring.profiles.active=dev demo-0.0.1-SNAPSHOT.jar
4、如果有文件application-default.properties、application-dev.properties
4.1 、在application-default.properties文件中配置spring.profiles.active=dev
在application-default.properties配置文件中
```
server.port=8081
spring.profiles.active=dev
def=def
other=other
```
在application-dev.properties配置文件中
```
server.port=8082
other=other2
```
则最终会有
```
server.port=8082
spring.profiles.active=dev
def=def
other=other2
```
4.2 、如果在同时在4.1的基础上再main方法中指定了spring.profiles.active=dev
,则只会加载application-dev.properties
public static void main(String[] args) throws Exception {
System.setProperty("spring.profiles.active", "dev");
SpringApplication.run(ClientApplication.class, args);
}
最终结果是
```
server.port=8082
other=other2
```
5、如果有文件application.properties、application-dev.properties、
则在application.properties文件中配置spring.profiles.active=dev
,同时在main中配置System.setProperty("spring.profiles.active", "dev");
会同时加载application.properties和特定文件
最终结果
server.port=8082
spring.profiles.active=dev
def=def
other=other2
如果不在application中指定spring.profiles.active=dev
则只会加载application.properties
用@Profile加载特定Bean
针对特定的环境加载Bean到容器中,Spring Profiles提供了一种隔离应用程序配置部分并使其仅在特定环境中可用的方法。任何@Component或@Configuration 可以标记@Profile以限制何时加载,如以下示例所示:
@Configuration
@Profile("production")
public class ProductionConfiguration {
}
总结
所以我们一般配置文件为
application.properteis
application-dev.properteis
application-test.properteis
在application.properteis指定spring.profiles.active=dev,如果发布则用java -jar -Dspring.profiles.active=test
在application.properties写默认配置,特定环境用来覆盖
【SpringBoot启动过程原理一 1.2.4节】: 中提到过Springboot在启动时会初始化环境变量
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments) {
// Create and configure the environment
// ⑴. 得到环境对象ConfigurableEnvironment,没有则创建一个StandardServletEnvironment
ConfigurableEnvironment environment = getOrCreateEnvironment();
// ⑵. 配置环境信息(激活环境,通过从系统环境变量里取);对listeners初始化环境属性
configureEnvironment(environment, applicationArguments.getSourceArgs());
// ⑶. 发布ApplicationEnvironmentPreparedEvent事件,加载配置文件,具体请看(ConfigFileApplicationListener)。
listeners.environmentPrepared(environment);
if (isWebEnvironment(environment) && !this.webEnvironment) {
environment = convertToStandardEnvironment(environment);
}
return environment;
}
在步骤(3)中会发布一个事件件,该事件会在ConfigFileApplicationListener监听器中处理,具体请看ConfigFileApplicationListener类,最终会调用ConfigFileApplicationListener中的内部类Loader加载配置信息
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,
addToLoaded(MutablePropertySources::addLast, false));
this.processedProfiles.add(profile);
}
resetEnvironmentProfiles(this.processedProfiles);
load(null, this::getNegativeProfileFilter,
addToLoaded(MutablePropertySources::addFirst, true));
addLoadedPropertySources();
}
private void initializeProfiles() {
// The default profile for these purposes is represented as null. We add it
// first so that it is processed first and has lowest priority.
// 增加一个null的环境变量
this.profiles.add(null);
// 从当前系统环境变量里获取激活的环境
Set<Profile> activatedViaProperty = getProfilesActivatedViaProperty();
// 获取排除系统环境变量里的激活环境信息设置SpringApplication.setAdditionalProfiles("");
this.profiles.addAll(getOtherActiveProfiles(activatedViaProperty));
// 合并以上得到的激活环境信息,可以看出来从系统环境变量得到的会在之后加载,所以会覆盖以前的
addActiveProfiles(activatedViaProperty);
// 如果profiles长度为1,则设置一个default激活环境
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);
}
}
}
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(
(name) -> load(location, name, profile, filterFactory, consumer));
});
}
private Set<String> getSearchLocations() {
// 如果是环境变量中配置的有spring.config.location,则直接返回该地址
if (this.environment.containsProperty(CONFIG_LOCATION_PROPERTY)) {
return getSearchLocations(CONFIG_LOCATION_PROPERTY);
}
// 如果是环境变量中没有配置spring.config.location,则先从spring.config.additional-location中加载,再从默认中加载
Set<String> locations = getSearchLocations(
CONFIG_ADDITIONAL_LOCATION_PROPERTY);
locations.addAll(asResolvedSet(ConfigFileApplicationListener.this.searchLocations,DEFAULT_SEARCH_LOCATIONS));
return locations;
}
private Set<String> getSearchNames() {
// 如果配置了spring.config.name直接返回,不在启用默认的
if (this.environment.containsProperty(CONFIG_NAME_PROPERTY)) {
String property = this.environment.getProperty(CONFIG_NAME_PROPERTY);
return asResolvedSet(property, null);
}
return asResolvedSet(ConfigFileApplicationListener.this.names, DEFAULT_NAMES);
}
使用YAML
加载YAML
Spring Framework提供了两个方便的类,可用于加载YAML文档。该YamlPropertiesFactoryBean负载YAML作为Properties和YamlMapFactoryBean 负载YAML作为Map。
- YAML示例
environments:
dev:
url: http://dev.example.com
name: Developer Setup
prod:
url: http://another.example.com
name: My Cool App
前面的示例将转换为以下属性:
environments.dev.url=http://dev.example.com
environments.dev.name=Developer Setup
environments.prod.url=http://another.example.com
environments.prod.name=My Cool App
- YAML列表表示为具有[index]解除引用的属性键。例如,考虑以下YAML:
my:
servers:
- dev.example.com
- another.example.com
前面的示例将转换为这些属性:
my.servers[0]=dev.example.com
my.servers[1]=another.example.com
- 绑定到Map属性时,如果key包含除小写字母数字字符以外的任何内容,则-需要使用括号表示法以保留原始值。如果密钥未被包围[],则任何非字母数字或-删除的字符。例如,考虑将以下属性绑定到Map:
acme:
map:
“[/ key1]”:value1
“[/ key2]”:value2
/ key3:value3
上面的属性将绑定到Mapwith /key1,/key2并key3作为地图中的键。
- 合并复杂类型
acme:
map:
key1:
name:my name 1
description:my description 1
---
spring:
profiles:dev
acme:
map:
key1:
name:dev name 1
key2:
name:dev name 2
description:dev description 2
如果dev配置文件未激活,则AcmeProperties.map包含一个带密钥的条目key1 (名称my name 1和描述my description 1)。dev但是,如果启用了配置文件,则map包含两个带有键的条目key1 (名称dev name 1和描述my description 1)和 key2(名称dev name 2和描述dev description 2)。
AML缺点
使用@PropertySource注释无法加载YAML文件。因此,如果您需要以这种方式加载值,则需要使用属性文件
在Spring Boot可以通过@ConfigurationProperties绑定到属性,具体请看SpringBoot中@ConfigurationProperties(四)