SpringBoot 容许你将配置外部化(从jar包外引用配置),所以,你可以将同一份代码运行到不同的环境中。你可以使用 properties 文件、YAML文件,environment 变量 和 命令行的方式进行外部化配置。配置文件中的值可以通过 @Value 注解直接注入到 Bean 中,也可以使用 Spring Environment 进行访问,或者通过使用 @ConfigurationProperties 注解绑定到结构化对象。
Spring Boot使用一种非常特殊的PropertySource顺序,其设计目的是允许合理地覆盖值。
- Devtools global settings properties in the $HOME/.config/spring-boot when devtools is active。
- @TestPropertySource annotations on your tests.
- @TestPropertySource annotations on your tests
- Command line arguments;
- Properties from SPRING_APPLICATION_JSON (inline JSON embedded in an environment variable or system property).
- ServletConfig init parameters.
- ServletContext init parameters.
- JNDI attributes from java:comp/env.
- Java System properties (System.getProperties()).
- OS environment variables
- A RandomValuePropertySource that has properties only in random.*.
- Profile-specific application properties outside of your packaged jar (application-{profile}.properties and YAML variants).
- Profile-specific application properties packaged inside your jar (application-{profile}.properties and YAML variants).
- Application properties outside of your packaged jar (application.properties and YAML variants).
- Application properties packaged inside your jar (application.properties and YAML variants).
- @PropertySource annotations on your @Configuration classes. Please note that such property sources are not added to the Environment until the application context is being refreshed. This is too late to configure certain properties such as logging.* and spring.main.* which are read before refresh begins.
- Default properties (specified by setting SpringApplication.setDefaultProperties).
下面提供一个具体的示例,假设你开发一个 @Component 组件使用 name 属性,如下代码片段所示:
import org.springframework.stereotype.*;
import org.springframework.beans.factory.annotation.*;
@Component
public class MyBean {
@Value("${name}")
private String name;
// ...
}
在你的类路径中(jar 包内),你可以有一个 application.properties 文件里面为 name 属性提供一个合理的默认值。当运行在一个新的环境中时,application.properties 文件可以被外置的配置文件替换。你启动的时候可以通过命令行替换name的值(java -jar app.jar --name="Spring")。
SPRING_APPLICATION_JSON 属性可以通过命令行设置环境变量,示例,
$ SPRING_APPLICATION_JSON='{"acme":{"name":"test"}}' java -jar myapp.jar
在前面的示例中,您将在Spring环境中使用acme.name=test。您还可以将JSON作为spring.application提供。系统属性中的json,如下面的示例所示
$ java -Dspring.application.json='{"name":"test"}' -jar myapp.jar
You can also supply the JSON by using a command line argument, as shown in the following example:
$ java -jar myapp.jar --spring.application.json='{"name":"test"}'
You can also supply the JSON as a JNDI variable, as follows: java:comp/env/spring.application.json.
2.1 Configuring Random Values
RandomValuePropertySource 可以注入随机数。它可以支持 integers、longs、uuids、strings,如下代码片段所示:
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]}
The random.int* syntax is OPEN value (,max) CLOSE where the OPEN,CLOSE are any character and value,max are integers. If max is provided, then value is the minimum value and max is the maximum value (exclusive)
random.int *语法是OPEN值(,max)CLOSE,其中OPEN,CLOSE是任何字符,value,max是整数。 如果提供了max,则value是最小值,而max是最大值(不包括)。
2.2 Accessing Command Line Properties
默认情况下,SpringApplication 会将所有的命令行参数(以--为前缀,比如 --server.port=9000)转换为属性并添加到 Environment中。如前面所述,命令行属性的优先级高于其他数据源的优先级。
如果你不想要命令行属性添加到 Environment 环境变量,你可以通过 SpringApplication.setAddCommandLineProperties(false) 方法关闭此功能。
2.3 Application Property Files
SpringApplication 从如下地址的 application.properties 文件加载属性并添加到 Environment 环境中。
- 当前目录下的 config 目录
- 当前目录
- classpath 下的 /config 目录
- classpath 根目录
上述配置文件地址列表加载顺序是从上到下加载。
你可以使用 yaml 文件替换 properties 文件
如果你不喜欢 application.properties 文件名称,你也可以使用 spring.config.name 命令指定文件名,你也可以使用 spring.config.location 属性指定文件路径。下面示例显示如何指定一个其他的文件名称。
java -jar myproject.jar --spring.config.name=myproject
下面示例实现了如何指定了两个地址
$ java -jar myproject.jar --spring.config.location=classpath:/default.properties,classpath:/override.properties
spring.config.name 和 spring.config.location 配置文件告诉系统需要加载哪个配置文件,他们必须指定一个 environment properties(OS environment variable,a system property,command-line)
如果 spring.config.location 中包含目录(非具体文件),需要以 / 结尾(并且在运行时会自动追加 spring.config.name 的名称).spring.config.location 指定的文件会按原样使用,不支持任何形式的重写和覆盖。
配置文件路径按照相反的顺序进行搜索。默认情况下,配置文件地址的顺序如下所示:
1. classpath:/,
2. classpath:/config/,
3. file:./,
4. file:./config/ .
而最后的搜索顺序如下所示:
1. file:./config/
2. file:./
3. classpath:/config/
4. classpath:/
当使用 spring.config.location 属性自定义配置文件地址时,它将会替代默认的文件地址。示例,如果 spring.config.location 的配置地址是 classpath:/custom-config/ , file:./custom-config/, 那搜索顺序将变为如下:
- file:./custom-config/
- classpath:custom-config/
当使用 spring.config.additional-location 属性时,它将会追加到默认的地址后面。追加的地址会优先于默认地址。示例,比如追加地址是 classpath:/custom-config/,file:./custom-config ,那扫描顺序则如下所示:
- file:./custom-config/
- classpath:custom-config/
- file:./config/
- file:./
- classpath:/config/
- classpath:/
通过此种搜索排序顺序,您可以在一个配置文件中指定默认值,然后在另一个配置文件中有选择地覆盖这些值。你可以在默认的路径中在 application.properties 中进行默认值的配置。这些默认值可以在运行时被其他自定义的地址覆盖。
如果你使用的是环境变量而不是系统属性,大多数的系统是不支持 逗号分隔符的,但是你可使用下划线进行替换,比如: SPRING_CONFIG_NAME instead of spring.config.name
如果您的应用程序在容器中运行,则可以使用JNDI属性(在java:comp / env中)或servlet上下文初始化参数代替环境变量或系统属性。
2.4 Profile-specific Properties
除了使用 application.properties 文件外,profile-specific properties 也可以按照如下命名规范来定义属性文件:
application-{profile}.properties
在 Environment 环境中,如果没有设置策略文件的情况下会默认提供一个default文件,换言之,系统会从 application-default.properties 文件加载属性。
Profile-specific 属性文件会从 application.properties 所在目录进行加载,profile-specific 文件总是会覆盖非 profile-specific ,不管 profile-specific 文件在jar包内或jar包外。
如果指定了多个配置文件,则采用后赢策略。 例如,在通过SpringApplication API配置的配置文件之后,添加了 spring.profiles.active 属性指定的配置文件具有优先权。
如果使用 spring.config.location 指定的文件路径,那任何路径外的配置文件都不会被考虑使用,不过可以添加到 spring.config.location 路径中使配置生效。
2.5 Placeholders in Properties
application.properties 中的属性在被使用的时候会被当前 Environment 过滤,所有你可以引用一些之前定义的值(for example,form System properties).
app.name=MyApp
app.description=${app.name} is a Spring Boot application
You can also use this technique to create “short” variants of existing Spring Boot properties. See the Howto.html how-to for details.
2.6 Encryping Properties
SpringBoot 不支持任何内置的属性加密技术,但是,它提供了修改 Spring Environment 中属性值得挂载点。EnvironmentPostProcessor 接口容许你在程序启动之前操作 Environment 属性。看考详细信息hwoto.html
如果你想要找一种安全的方式来存储证书和密码,你可以使用 Spring Cloud Vault 项目来存储外部配置到 HashiCorp Vault
2.7 Using YAML Instead of Properties
YAML是JSON的超集,因此是一种用于指定层次结构配置数据的便捷格式。 只要在类路径上具有SnakeYAML库,SpringApplication类就会自动支持YAML作为属性的替代方法。
If you use “Starters”, SnakeYAML is automatically provided by spring-boot-starter.
2.7.1 Loading YAML
Spring Framework 提供两个方便的类来加载 YAML 文件。YamlPropertiesFactoryBean 将 YAML 作为属性文件进行加载,YamlMapFactoryBean 将 YAML 作为 Map 加载。
YamlPropertiesFactoryBean
environments:
dev:
url: https://dev.example.com
name: Developer Setup
prod:
url: https://another.example.com
name: My Cool App
上面内容将转换为如下属性
environments.dev.url=https://dev.example.com
environments.dev.name=Developer Setup
environments.prod.url=https://another.example.com
environments.prod.name=My Cool App
YamlMapFactoryBean
YAML 清单的属性元素可以使用 [index] 替换,如下所示:
my:
servers:
- dev.example.com
- another.example.com
my.servers[0]=dev.example.com
my.servers[1]=another.example.com
可以使用 Spring Boot‘s Binder 工具来绑定属性(参考 @ConfigurationProperties),在目标对象中你需要有一个 List 或者 Set 对象并且需要提供 setter 或者初始化方法。参考如下示例,绑定了上面定义的属性到对象:
@ConfigurationProperties(prefix="my")
public class Config {
private List<String> servers = new ArrayList<String>();
public List<String> getServers() {
return this.servers;
}
}
2.7.2 Exposing YAML as Properties in the spring Envronment
YamlPropertySourceLoader 类可以在 Spring Environment 环境中以 PropertySource 的形式暴露 YAML 属性,所以你可以使用 @Value 注解及占位符来访问 YAML 属性。
2.7.3 Multi-profile YAML Documents
在 单个 YAML 文档中你可以使用 profile-specific 关键字对多个文档内容进行区分,如下所示:
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
在上面的代码片段中,如果 development 配置激活,server.address 的地址是 127.0.0.1,同样的,如果 production and eu-central 配置激活,server.address 属性是 192.168.1.120,如果 development、production、eu-central 都没有激活,server.address 地址是 192.168.1.100
如果在启动应用程序上下文时未显式激活任何配置,则会激活默认配置文件。 因此,在以下YAML中,我们为spring.security.user.password设置了一个值,该值仅在“默认”配置文件中可用
server:
port: 8000
---
spring:
profiles: default
security:
user:
password: weak
而在下面配置中,密码将会一直被使用因为它部署任何配置,并且你可以在需要的时候显示的进行重写。
server:
port: 8000
spring:
security:
user:
password: weak
Spring profiles designated by using the spring.profiles element may optionally be negated by using the ! character. If both negated and non-negated profiles are specified for a single document, at least one non-negated profile must match, and no negated profiles may match.
2.7.4 YAML Shortcomings
YAML 文件不能使用 @PropertySource 注解进行加载。所有,在这种情况下你需要使用 properties 属性文件。
Using the multi YAML document syntax in profile-specific YAML files can lead to unexpected behavior. For example, consider the following config in a file:
application-dev.yml
server:
port: 8000
---
spring:
profiles: "!test"
security:
user:
password: "secret"
如果使用参数--spring.profiles.active = dev运行应用程序,则可能希望将security.user.password设置为“ secret”,但事实并非如此。
当前文档的嵌套结构将会被忽略因为当前文件的名称是 application-dev.yml。它已经是一个 profile-specific 文件并且结构将会被忽略。
We recommend that you don’t mix profile-specific YAML files and multiple YAML documents. Stick to using only one of them.
不建议混合的 profile-specific yaml 文件和 多个 yaml 文档一起使用,选择其一即可。
2.8 Type-sage Configuration Properties
在某些情况下使用 @Value("${}") 注解进行配置属性的注入有些麻烦,尤其是在多配置文件或者你的数据结构有等级限制。Spring Boot提供了另一种处理属性的方法,允许强类型bean控制和验证应用程序的配置。
关于 @Value 和 Type-sage Configuration Properties 的区别请参考 2.8.10 章节
2.8.1 JavaBean properties binding
绑定属性到一个标准的 JavaBean 属性,如下所示:
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("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
SpringBoot 中绑定属性到 @ConfigurationProperties 注解的类中的变量,可以使用 properties files、YAML files、environment vriables 等,但是该类的(settter/getter)方法不能直接访问。
这种安排依赖于默认的空构造函数,getter和setter通常是必需的,因为绑定是通用的标准的Java bean属性描述符进行的,就像Spring MVC一样。在下列情况下,可以省略setter方法;
- Maps, as long as they are initialized, need a getter but not necessarily a setter, since they can be mutated by the binder.
- Collections and arrays can be accessed either through an index (typically with YAML) or by using a single comma-separated value (properties). In the latter case, a setter is mandatory. We recommend to always add a setter for such types. If you initialize a collection, make sure it is not immutable (as in the preceding example).
- If nested POJO properties are initialized (like the Security field in the preceding example), a setter is not required. If you want the binder to create the instance on the fly by using its default constructor, you need a setter.
Some people use Project Lombok to add getters and setters automatically. Make sure that Lombok does not generate any particular constructor for such a type, as it is used automatically by the container to instantiate the object.
有些人使用Lombok项目自动添加获取器和设置器。 确保Lombok不会为这种类型生成任何特定的构造函数,因为容器会自动使用它来实例化该对象。
最后,仅考虑标准Java Bean属性,并且不支持对静态属性的绑定。
2.8.2 Constructor binding
上一节中的示例可以用不可变的方式重写,如下面的示例所示:
package com.example;
import java.net.InetAddress;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConstructorBinding;
import org.springframework.boot.context.properties.DefaultValue;
@ConstructorBinding
@ConfigurationProperties("acme")
public class AcmeProperties {
private final boolean enabled;
private final InetAddress remoteAddress;
private final Security security;
public AcmeProperties(boolean enabled, InetAddress remoteAddress, Security security) {
this.enabled = enabled;
this.remoteAddress = remoteAddress;
this.security = security;
}
public boolean isEnabled() { ... }
public InetAddress getRemoteAddress() { ... }
public Security getSecurity() { ... }
public static class Security {
private final String username;
private final String password;
private final List<String> roles;
public Security(String username, String password,
@DefaultValue("USER") List<String> roles) {
this.username = username;
this.password = password;
this.roles = roles;
}
public String getUsername() { ... }
public String getPassword() { ... }
public List<String> getRoles() { ... }
}
}
在此设置中,@ConstructorBinding批注用于指示应使用构造函数绑定。 这意味着绑定器将期望找到带有您希望绑定的参数的构造函数。
@ConstructorBinding类的嵌套成员(如上面示例中的安全性)也将通过其构造函数绑定。
Default values can be specified using @DefaultValue and the same conversion service will be applied to coerce the String value to the target type of a missing property.
如果您的类具有多个构造函数,则还可以直接在应绑定的构造函数上使用@ConstructorBinding。
要使用构造函数绑定,必须使用@EnableConfigurationProperties或配置属性扫描来启用该类。如果当前Bean 是根据 spring 机制创建的则不能使用构造函数绑定(e.g. @Component beans, beans created via @Bean methods or beans loaded using @Import)
2.8.3 Enabling @ConfigurationProperties-annotatedtypes
SpringBoot 提供了基础设施来绑定 @ConfigurationProperties 类型和注册成为bean。你可以按照类逐个扫描也可以按照组件扫描的方式进行启用。
有些时候,添加 @ConfigurationProperties 注解的类可能不适用以扫描的方式注册为bean。比如,你需要自定义注解或者按条件的启用。在这种情况下,使用 @EnableConfigurationProperties 注解指定需要处理的列表。这个可以应用在任何标有 @Configuration 注解的类上,示例如下:
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(AcmeProperties.class)
public class MyConfiguration {
}
要使用配置属性扫描,添加 @ConfigurationPropertiesSan 注解到你的主程序。通常情况下,你可以添加这个注解到带有 @SpringBootApplication 的主类中或者添加到带有 @Configuration 的类中。默认情况下,将会对添加注解的包进行扫描,如果你想要自定义扫描路径,你可参考如下示例配置:
@SpringBootApplication
@ConfigurationPropertiesScan({ "com.example.app", "org.acme.another" })
public class MyApplication {
}
使用配置属性扫描或通过@EnableConfigurationProperties注册@ConfigurationProperties Bean时,该Bean具有常规名称:<prefix>-<fqn>,其中<prefix>是@ConfigurationProperties指定的前缀和<fqn>是Bean的完全限定名称。如果 ConfigurationProperties 不提供任何前缀,则仅使用Bean的完全限定名称。
The bean name in the example above is acme-com.example.AcmeProperties
We recommend that @ConfigurationProperties only deal with the environment and, in particular, does not inject other beans from the context. For corner cases, setter injection can be used or any of the *Aware interfaces provided by the framework (such as EnvironmentAware if you need access to the Environment). If you still want to inject other beans using the constructor, the configuration properties bean must be annotated with @Component and use JavaBean-based property binding.
2.8.4 Using @ConfigurationProperties-annotated types
这种配置样式与SpringApplication外部YAML配置特别有效,如以下示例所示:
application.yml
acme:
remote-address: 192.168.1.1
security:
username: admin
roles:
- USER
- ADMIN
additional configuration as required
要使用@ConfigurationProperties Bean,可以像其他任何Bean一样注入它们,如以下示例所示:
@Service
public class MyService {
private final AcmeProperties properties;
@Autowired
public MyService(AcmeProperties properties) {
this.properties = properties;
}
//...
@PostConstruct
public void openConnection() {
Server server = new Server(this.properties.getRemoteAddress());
// ...
}
}
Using @ConfigurationProperties also lets you generate metadata files that can be used by IDEs to offer auto-completion for your own keys. See the appendix for details.
2.8.5 Thrid-party Configuration
除了使用@ConfigurationProperties来注释一个类外,您还可以在公共@Bean方法上使用它。当您希望将属性绑定到您无法控制的第三方组件时,这样做特别有用。
要从环境属性配置bean,请将@ConfigurationProperties添加到它的bean注册中,如下面的示例所示
@ConfigurationProperties(prefix = "another")
@Bean
public AnotherComponent anotherComponent() {
...
}
2.8.6 Relaxed Binding
SpringBoot 为 属性变量绑定到具体的bean提供了比较宽松的规则,所以,属性文件名称和 Environment 的属性名称不需要完全一致,比如,context-path 可以绑定到 contextPath。参考如下示例:
@ConfigurationProperties(prefix="acme.my-project.person")
public class OwnerProperties {
private String firstName;
public String getFirstName() {
return this.firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
}
With the preceding code, the following properties names can all be used:
Property | Note |
---|---|
acme.my-project.person.first-name | 推荐在.properties和.yml文件中使用 |
acme.myProject.person.firstName | 标准的驼峰匹配语法 |
acme.my_project.person.first_name | 下划线表示法,它是.properties和.yml文件中使用的另一种格式 |
ACME_MYPROJECT_PERSON_FIRSTNAME | 大写格式,在使用系统环境变量时推荐使用这种格式 |
The prefix value for the annotation must be in kebab case (lowercase and separated by -, such as acme.my-project.person).
Property Source | Simple | List |
---|---|---|
Properties Files | Camel case, kebab case, or underscore notation | 使用[]或逗号分隔值的标准列表语法 |
YAML Files | Camel case, kebab case, or underscore notation | 标准的YAML列表语法或逗号分隔的值 |
Environment Variables | 用下划线作为分隔符的大写格式。不应该在属性名中使用 | Numeric values surrounded by underscores, such as MY ACME 1 OTHER = my.acme[1].other |
System properties | Camel case, kebab case, or underscore notation | 使用[]或逗号分隔值的标准列表语法 |
我们建议,如果可能的话,将属性存储为小写的kebab格式,例如my.property-name=acme。
绑定到Map属性时,如果键包含小写字母数字字符或-以外的任何其他字符,则需要使用方括号表示法,以便保留原始值。 如果键没有被[]包围,则所有非字母数字或-的字符都将被删除。 例如,考虑将以下属性绑定到Map:
acme:
map:
"[/key1]": value1
"[/key2]": value2
/key3: value3
The properties above will bind to a Map with /key1, /key2 and key3 as the keys in the map.
对于YAML文件,方括号需要用引号括起来,以便正确地解析
2.8.7 Merging Complex Types
如果在多个位置配置了列表,则通过替换整个列表来进行覆盖,在下面的示例中,MyPojo 对象有 name 和 description 两个属性且默认值为null。从 AcmeProperties 对象包暴露 MyPojo的对象列表。
@ConfigurationProperties("acme")
public class AcmeProperties {
private final List<MyPojo> list = new ArrayList<>();
public List<MyPojo> getList() {
return this.list;
}
}
参考下面的配置信息
acme:
list:
- name: my name
description: my description
---
spring:
profiles: dev
acme:
list:
- name: my another name
如果 dev 选项没有激活,AcmeProperties.list 包含一个 MyPojo 实体。如果 dev 选项激活,list 属性任然包含一个属性(name 的值是 my another name ,description 的值是 null)。这个配置不会添加第二个 MyPojo 实例到 list,并且不会进行覆盖。
当进行了多个 list 配置时,将使用优先级最高的且仅使用优先级最高的,参考下面示例代码:
acme:
list:
- name: my name
description: my description
- name: another name
description: another description
---
spring:
profiles: dev
acme:
list:
- name: my another name
在上述示例中,如果 dev 选项激活,AcmeProperties.list 中将包含一个 MyPojo 实体(name = my another name ,description = null). 对于YAML,可以使用逗号分隔的列表和YAML列表来完全覆盖列表的内容。
对于 Map 属性,你可以从多个数据源进行属性的绑定。然而,对于多个数据源的一个属性,会使用优先级最高的属性值。下面示例将暴露一个 Map<String, MyPojo> 从 AcmeProperties 对象。
@ConfigurationProperties("acme")
public class AcmeProperties {
private final Map<String, MyPojo> map = new HashMap<>();
public Map<String, MyPojo> getMap() {
return this.map;
}
}
参考如下配置信息
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(name = my name 1,description = my description 1)。如果 dev 选项激活,map 将包含两个实体 key1(name = dev name 1,description = my description 1) 和 key2 (name = dev name 2,description = dev description 2)
前述合并规则不仅适用于YAML文件,而且适用于所有 property 属性。
2.8.8 Properties Conversion
当Spring Boot绑定到@ConfigurationProperties bean时,它尝试将外部应用程序属性强制转换为正确的类型.如果你想自定义转换规则,你可以提供一个 ConversionService bean 或者 自定义 editors 属性 或者 自定义 Converters。
由于在应用程序生命周期中非常早就请求了此bean,因此请确保限制您的ConversionService使用的依赖项。 通常,您需要的任何依赖项在创建时可能都没有完全初始化。 如果配置键强制不需要自定义ConversionService,而仅依赖于具有@ConfigurationPropertiesBinding限定的自定义转换器,则可能需要重命名自定义ConversionService。
2.8.8.1 Converting durations
SpringBoot 在时间转换上有专用的支持,如果你暴露一个 java.time.Duration 属性,下面格式在 application.properties 中可以生效。
- A regular long representation (using milliseconds as the default unit unless a @DurationUnit has been specified)
- The standard ISO-8601 format used by java.time.Duration
- A more readable format where the value and the unit are coupled (e.g. 10s means 10 seconds)
参考下面代码片段
@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;
}
}
To specify a session timeout of 30 seconds, 30, PT30S and 30s are all equivalent. A read timeout of 500ms can be specified in any of the following form: 500, PT0.5S and 500ms.
You can also use any of the supported units. These are:
- ns for nanoseconds
- us for microseconds
- ms for milliseconds
- s for seconds
- m for minutes
- h for hours
- d for days
The default unit is milliseconds and can be overridden using @DurationUnit as illustrated in the sample above
2.8.9 @ConfigurationProperties Validation
SpringBoot 尝试着验证 @ConfigurationProperties 注解配置的类当此类同时也配置了 @Validated 注解。You can use JSR-303 javax.validation constraint annotations directly on your configuration class. 为此,请确保classpath上有一个兼容的JSR-303实现,然后将约束注释添加到字段中,如下面的示例所示
@ConfigurationProperties(prefix="acme")
@Validated
public class AcmeProperties {
@NotNull
private InetAddress remoteAddress;
// ... getters and setters
}
You can also trigger validation by annotating the @Bean method that creates the configuration properties with @Validated.
为了确保即使在没有找到属性的情况下依然可以触发属性的验证机制,必须要在相关的字段上添加 @Valid 注解。下面的示例建立在前面的AcmeProperties示例的基础上。
@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 来自定义 Spring Validator。@Bean 方法应该定义为静态的。配置属性验证器在程序生命周期内早期就被创建,并且 @Bean 方法已静态的方式被创建且不需要实例化 @Configuration 类。这样做可以避免任何可能由早期实例化引起的问题。
The spring-boot-actuator module includes an endpoint that exposes all @ConfigurationProperties beans. Point your web browser to /actuator/configprops or use the equivalent JMX endpoint. See the "Production ready features" section for details.
2.8.10 @ConfigurationProperties vs. @Value
@Value注解是容器的核心功能,它不提供与类型安全(type-safe)配置属性相同的特性.The following table summarizes the features that are supported by @ConfigurationProperties and @Value:
Feature | @ConfigurationProperties | @Valu |
---|---|---|
Relaxed binding | yes | no |
Meta-data support | yes | no |
SpEL evaluation | no | yes |
如果您为自己的组件定义了一组配置键,我们建议您将它们组合在以@ConfigurationProperties注释的POJO中。 您还应该意识到,由于@Value不支持宽松的绑定,因此如果您需要使用环境变量来提供值,则它不是一个很好的选择。
最后,尽管您可以在@Value中编写SpEL表达式,但不会从应用程序属性文件中处理此类表达式。