SpringBoot 之属性加载详解
# SpringBoot 之属性加载详解
# 加载 property 顺序
Spring Boot 加载 property 顺序如下:
- Devtools 全局配置 (opens new window) (当 devtools 被激活
~/.spring-boot-devtools.properties
). - 测试环境中的
@TestPropertySource
注解配置 (opens new window) - 测试环境中的属性
properties
:@SpringBootTest
(opens new window) 和 测试注解 (opens new window). - 命令行参数
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
指定)
# 随机属性
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]}
# 命令行属性
默认情况下, SpringApplication
会获取 --
参数(例如 --server.port=9000
),并将这个 property
添加到 Spring 的 Environment
中。
如果不想加载命令行属性,可以通过 SpringApplication.setAddCommandLineProperties(false)
禁用。
# Application 属性文件
SpringApplication
会自动加载以下路径下的 application.properties
配置文件,将其中的属性读到 Spring 的 Environment
中。
- 当前目录的
/config
子目录 - 当前目录
- classpath 路径下的
/config
package - classpath 根路径
注:
以上列表的配置文件会根据顺序,后序的配置会覆盖前序的配置。
你可以选择 YAML(yml) (opens new window) 配置文件替换 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
# Profile 特定属性
如果定义 application-{profile}.properties
形式的配置文件,将被视为 profile
环境下的特定配置。
可以通过 spring.profiles.active
参数来激活 profile,如果没有激活的 profile,默认会加载 application-default.properties
中的配置。
# 属性中的占位符
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 (opens new window)
# 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
# 访问属性
YamlPropertySourceLoader
类会将 YAML 配置转化为 Spring Environment
类中的 PropertySource
。然后,你可以如同 properties 文件中的属性一样,使用 @Value
注解来访问 YAML 中配置的属性。
# 多 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
# YAML 的缺点
注:YAML 注解中的属性不能通过 @PropertySource
注解来访问。所以,如果你的项目中使用了一些自定义属性文件,建议不要用 YAML。
# 属性前缀
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 {
}
# 属性松散绑定规则
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 | 大写字母 |
# 属性转换
如果需要类型转换,你可以提供一个 ConversionService
bean (一个名叫 conversionService
的 bean) 或自定义属性配置 (一个 CustomEditorConfigurer
bean) 或自定义的 Converters
(被 @ConfigurationPropertiesBinding
注解修饰的 bena)。
# 时间单位转换
Spring 使用 java.time.Duration
类代表时间大小,以下场景适用:
- 除非指定
@DurationUnit
,否则一个 long 代表的时间为毫秒。 - ISO-8601 标准格式(
java.time.Duration
(opens new window) 的实现就是参照此标准) - 你也可以使用以下支持的单位:
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;
}
}
# 数据大小转换
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;
}
}
# 校验属性
@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
。