9. 属性加载
1. 加载 property 顺序
Spring Boot 加载 property 顺序如下:
- Devtools 全局配置 (当 devtools 被激活 ~/.spring-boot-devtools.properties).
- 测试环境中的 @TestPropertySource 注解配置
- 测试环境中的属性 properties:@SpringBootTest 和 测试注解.
- 命令行参数
- SPRING_APPLICATION_JSON 属性
- ServletConfig 初始化参数
- ServletContext 初始化参数
- JNDI attributes from 通过 java:comp/env 配置的 JNDI 属性
- Java 系统属性 (System.getProperties())
- 操作系统环境比那里
- RandomValuePropertySource 加载 random.* 形式的属性
- jar 包外的 application-{profile}.properties 或 application-{profile}.yml 配置
- jar 包内的 application-{profile}.properties 或 application-{profile}.yml 配置
- jar 包外的 application.properties 或 application.yml 配置
- jar 包内的 application.properties 或 application.yml 配置
- @PropertySource 绑定的配置
- 默认属性 (通过 SpringApplication.setDefaultProperties 指定)
2. 随机属性
RandomValuePropertySource 类用于配置随机值。
示例:
my.secret=${random.value}
my.number=${random.int}
my.bignumber=${random.long}
my.uuid=${random.uuid}
my.number.less.than.ten=${random.int(10)}
my.number.in.range=${random.int[1024,65536]}
3. 命令行属性
默认情况下, SpringApplication 会获取 – 参数(例如 --server.port=9000 ),并将这个 property 添加到 Spring 的 Environment 中。
如果不想加载命令行属性,可以通过 SpringApplication.setAddCommandLineProperties(false) 禁用。
4. Application 属性文件
SpringApplication 会自动加载以下路径下的 application.properties 配置文件,将其中的属性读到 Spring 的 Environment 中。
-
当前目录的 /config 子目录
-
当前目录
-
classpath 路径下的 /config package
-
classpath 根路径
注:
以上列表的配置文件会根据顺序,后序的配置会覆盖前序的配置。
你可以选择 YAML(yml) 配置文件替换 properties 配置文件。
如果不喜欢 application.properties 作为配置文件名,可以使用 spring.config.name 环境变量替换:
$ java -jar myproject.jar --spring.config.name=myproject
可以使用 spring.config.location 环境变量指定配置文件路径:
$ java -jar myproject.jar --spring.config.location=classpath:/default.properties,classpath:/override.properties
5. Profile 特定属性
如果定义 application-{profile}.properties 形式的配置文件,将被视为 profile 环境下的特定配置。
可以通过 spring.profiles.active 参数来激活 profile,如果没有激活的 profile,默认会加载 application-default.properties 中的配置。
6. 属性中的占位符
application.properties 中的值会被 Environment 过滤,所以,可以引用之前定义的属性。
app.name=MyApp
app.description=${app.name} is a Spring Boot application
注:你可以使用此技术来创建 Spring Boot 属性变量。请参考: Section 77.4, “Use ‘Short’ Command Line Arguments
7. YAML 属性
Spring 框架有两个类支持加载 YAML 文件。
- YamlPropertiesFactoryBean 将 YAML 文件的配置加载为 Properties 。
- YamlMapFactoryBean 将 YAML 文件的配置加载为 Map 。
示例 1
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 支持列表形式,等价于 property 中的 [index] :
my:
servers:
- dev.example.com
- another.example.com
等价于
my.servers[0]=dev.example.com
my.servers[1]=another.example.com
7.1. 访问属性
YamlPropertySourceLoader 类会将 YAML 配置转化为 Spring Environment 类中的 PropertySource 。然后,你可以如同 properties 文件中的属性一样,使用 @Value 注解来访问 YAML 中配置的属性。
7.2. 多 profile 配置
server:
address: 192.168.1.100
---
spring:
profiles: development
server:
address: 127.0.0.1
---
spring:
profiles: production & eu-central
server:
address: 192.168.1.120
7.3. YAML 的缺点
注:YAML 注解中的属性不能通过 @PropertySource 注解来访问。所以,如果你的项目中使用了一些自定义属性文件,建议不要用 YAML。
8. 属性前缀
package com.example;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix="acme")
public class AcmeProperties {
private boolean enabled;
private InetAddress remoteAddress;
private final Security security = new Security();
public boolean isEnabled() { ... }
public void setEnabled(boolean enabled) { ... }
public InetAddress getRemoteAddress() { ... }
public void setRemoteAddress(InetAddress remoteAddress) { ... }
public Security getSecurity() { ... }
public static class Security {
private String username;
private String password;
private List<String> roles = new ArrayList<>(Collections.singleton("USER"));
public String getUsername() { ... }
public void setUsername(String username) { ... }
public String getPassword() { ... }
public void setPassword(String password) { ... }
public List<String> getRoles() { ... }
public void setRoles(List<String> roles) { ... }
}
}
相当于支持配置以下属性:
- acme.enabled
- acme.remote-address
- acme.security.username
- acme.security.password
- acme.security.roles
然后,你需要使用 @EnableConfigurationProperties 注解将属性类注入配置类中。
@Configuration
@EnableConfigurationProperties(AcmeProperties.class)
public class MyConfiguration {
}
9. 属性松散绑定规则
Spring Boot 属性名绑定比较松散。
以下属性 key 都是等价的:
Property Note
acme.my-project.person.first-name
- 分隔
acme.myProject.person.firstName 驼峰命名
acme.my_project.person.first_name _ 分隔
ACME_MYPROJECT_PERSON_FIRSTNAME 大写字母
10. 属性转换
如果需要类型转换,你可以提供一个 ConversionService bean (一个名叫 conversionService 的 bean) 或自定义属性配置 (一个 CustomEditorConfigurer bean) 或自定义的 Converters (被 @ConfigurationPropertiesBinding 注解修饰的 bena)。
10.1. 时间单位转换
Spring 使用 java.time.Duration 类代表时间大小,以下场景适用:
- 除非指定 @DurationUnit ,否则一个 long 代表的时间为毫秒。
- ISO-8601 标准格式( java.time.Duration 的实现就是参照此标准)
- 你也可以使用以下支持的单位:
- ns - 纳秒
- us - 微秒
- ms - 毫秒
- s - 秒
- m - 分
- h - 时
- d - 天
示例:
@ConfigurationProperties("app.system")
public class AppSystemProperties {
@DurationUnit(ChronoUnit.SECONDS)
private Duration sessionTimeout = Duration.ofSeconds(30);
private Duration readTimeout = Duration.ofMillis(1000);
public Duration getSessionTimeout() {
return this.sessionTimeout;
}
public void setSessionTimeout(Duration sessionTimeout) {
this.sessionTimeout = sessionTimeout;
}
public Duration getReadTimeout() {
return this.readTimeout;
}
public void setReadTimeout(Duration readTimeout) {
this.readTimeout = readTimeout;
}
}
10.2. 数据大小转换
Spring 使用 DataSize 类代表数据大小,以下场景适用:
- long 值(默认视为 byte)
- 你也可以使用以下支持的单位:
- B
- KB
- MB
- GB
- TB
示例:
@ConfigurationProperties("app.io")
public class AppIoProperties {
@DataSizeUnit(DataUnit.MEGABYTES)
private DataSize bufferSize = DataSize.ofMegabytes(2);
private DataSize sizeThreshold = DataSize.ofBytes(512);
public DataSize getBufferSize() {
return this.bufferSize;
}
public void setBufferSize(DataSize bufferSize) {
this.bufferSize = bufferSize;
}
public DataSize getSizeThreshold() {
return this.sizeThreshold;
}
public void setSizeThreshold(DataSize sizeThreshold) {
this.sizeThreshold = sizeThreshold;
}
}
11. 校验属性
@ConfigurationProperties(prefix="acme")
@Validated
public class AcmeProperties {
@NotNull
private InetAddress remoteAddress;
@Valid
private final Security security = new Security();
// ... getters and setters
public static class Security {
@NotEmpty
public String username;
// ... getters and setters
}
}
你也可以自定义一个名为 configurationPropertiesValidator 的校验器 Bean。获取这个 @Bean 的方法必须声明为 static。