2. 外部化配置
Spring Boot使您可以外部化配置,以便可以在不同环境中使用相同的应用程序代码。您可以使用properties 文件,YAML文件,环境变量和命令行参数来外部化配置。属性值可以通过 @Value注解
直接注入到你的beans,通过Spring的Environment
访问抽象,或者通过@ConfigurationProperties
绑定到结构化对象。
Spring Boot使用一个非常特殊的PropertySource
顺序,该顺序旨在允许合理地覆盖值。按以下顺序考虑属性:
-
当devtools处于活动状态时,文件夹
$HOME/.config/spring-boot
中的Devtools全局设置属性。 -
测试中的@TestPropertySource注解。
-
测试中的
properties
属性。可用于测试应用程序的特定部分的测试注解和@SpringBootTest注解。 -
命令行参数。
-
来自
SPRING_APPLICATION_JSON
的属性(嵌入在环境变量或系统属性中的嵌入式JSON)。 -
ServletConfig
初始化参数。 -
ServletContext
初始化参数。 -
java:comp/env
的JNDI属性。 -
Java系统属性(
System.getProperties()
)。 -
操作系统环境变量。
-
RandomValuePropertySource
,只有在random.*属性
。 -
打包的jar(
application-{profile}.properties
和YAML变量)之外的特定于配置文件的应用程序属性。 -
打包在jar中的特定于配置文件的应用程序属性(
application-{profile}.properties
和YAML变量)。 -
打包的jar之外的应用程序属性(
application.properties
和YAML变量)。 -
打包在jar中的应用程序属性(
application.properties
和YAML变量)。 -
@Configuration
类上的@PropertySource注解。请注意,在刷新应用程序上下文之前,不会将此类属性源添加到Environment
中。现在配置某些属性(如logging。*和spring.main。*)为时已晚,这些属性在刷新开始之前就已读取。 -
默认属性(通过设置
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
可以在jar外部提供一个文件,该文件将覆盖name
。对于一次性测试,您可以使用特定的命令行开关(例如,java -jar app.jar --name="Spring"
)启动。
这些
SPRING_APPLICATION_JSON
属性可以在命令行中提供环境变量。例如,您可以在UN * X shell中使用以下行:$ SPRING_APPLICATION_JSON='{"acme":{"name":"test"}}' java -jar myapp.jar
在前面的示例中,您最终
acme.name=test
在Spring中Environment
。您还可以像spring.application.json
在System属性中一样提供JSON ,如以下示例所示:$ java -Dspring.application.json='{"name":"test"}' -jar myapp.jar
您还可以使用命令行参数来提供JSON,如以下示例所示:
$ java -jar myapp.jar --spring.application.json ='{“ name”:“ test”}'
您还可以将JSON作为JNDI变量提供,如下所示:
java:comp/env/spring.application.json
。
2.1. 配置随机值
RandomValuePropertySource可用于注入随机值(例如,注入机密或测试用例)。它可以产生整数,longs,uuid或字符串,如以下示例所示:
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]}
该random.int*
语法是OPEN value (,max) CLOSE
其中的OPEN,CLOSE
任何字符和value,max
是整数。如果max
提供,value
则为最小值,max
为最大值(不包括)。
2.2. 访问命令行属性
默认情况下,SpringApplication
将所有命令行选项参数(即以--
开头的参数,例如--server.port=9000
)转换为property
并将其添加到Spring Environment
。如前所述,命令行属性始终优先于其他属性源。
如果不希望将命令行属性添加到中Environment
,则可以使用禁用它们SpringApplication.setAddCommandLineProperties(false)
。
2.3. 应用程序属性文件
SpringApplication
从application.properties
以下位置的文件加载属性并将其添加到Spring中Environment
:
-
一个
/config
当前目录的子目录 -
当前目录
-
类路径
/config
包 -
类路径根
该列表按优先级排序(在列表较高位置定义的属性会覆盖在较低位置定义的属性)。
您还可以使用YAML(.yml)文件来替代.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
很早就用于确定必须加载哪些文件。必须将它们定义为环境属性(通常是OS环境变量,系统属性或命令行参数)。
如果spring.config.location
包含目录(而不是文件),则应以目录结尾/
(在运行时,在目录后附加从其生成spring.config.name
之前生成的名称,包括特定于配置文件的文件名)。指定的文件spring.config.location
按原样使用,不支持特定于配置文件的变体,并且被任何特定于配置文件的属性覆盖。
配置位置以相反的顺序搜索。默认情况下,配置的位置是classpath:/,classpath:/config/,file:./,file:./config/
。结果搜索顺序如下:
-
file:./config/
-
file:./
-
classpath:/config/
-
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
)。然后,可以在运行时使用自定义位置之一中的其他文件覆盖这些默认值。
如果您使用环境变量而不是系统属性,则大多数操作系统都不允许使用句点分隔的键名,但是您可以使用下划线代替(例如,SPRING_CONFIG_NAME
代替spring.config.name
)。
如果您的应用程序在容器中运行,则可以使用JNDI属性(中的
java:comp/env
)或Servlet上下文初始化参数来代替环境变量或系统属性,也可以使用这些参数。
2.4. 特定于配置文件的属性
除了application.properties文件之外,还可以使用以下命名约定来定义特定于配置文件的属性:application- {profile} .properties。如果没有设置活动配置文件,则环境具有一组默认配置文件(默认为[default
])。换句话说,如果没有明确激活任何配置文件,则将加载来自application-default.properties的属性。
特定于配置文件的属性是从与standard相同的位置加载的application.properties
,特定于配置文件的文件始终会覆盖非特定文件,无论特定于配置文件的文件位于打包jar的内部还是外部。
如果指定了多个配置文件,则采用后赢策略。例如,由spring.profiles.active
属性指定的配置文件会在通过SpringApplication
API 配置的配置文件之后添加,因此具有优先权。
如果您在spring.config.location中指定了任何文件,则不会考虑这些文件的特定于配置文件的变体。如果您还想使用特定于配置文件的属性,请使用spring.config.location属性。
2.5. 属性中的占位符
使用时,application.properties中的值会通过现有环境进行过滤,因此您可以引用以前定义的值(例如,从系统属性中)。
app.name=MyApp
app.description=${app.name} is a Spring Boot application
您还可以使用此技术来创建现有Spring Boot属性的“简短”变体。有关详细信息,请参见 howto.html 操作方法。
2.6. 加密属性
Spring Boot不提供对加密属性值的任何内置支持,但是,它确实提供了修改Spring Environment
包含的值所必需的挂钩点。该EnvironmentPostProcessor接口
允许您在应用程序启动之前对Environment
进行操作。有关详细信息,请参见howto.html。
如果您正在寻找一种安全的方式来存储凭据和密码,Spring Cloud Vault项目提供了对在HashiCorp Vault中存储外部化配置的支持。
2.7. 使用 YAML 代替 Properties
YAML is a superset of JSON and, as such, is a convenient format for specifying hierarchical configuration data. The SpringApplication
class automatically supports YAML as an alternative to properties whenever you have the SnakeYAML library on your classpath.
YAML是JSON的超集,因此是一种用于指定层次结构配置数据的便捷格式。只要在类路径上具有SnakeYAML库,SpringApplication类就会自动支持YAML作为属性的替代方法。
如果您使用“
starter
”,则SnakeYAML将由自动提供spring-boot-starter
。
2.7.1. 加载YAML
Spring Framework提供了两个方便的类,可用于加载YAML文档。该YamlPropertiesFactoryBean把
YAML作为Properties加载,
YamlMapFactoryBean把
YAML作为Map加载
。
例如,考虑以下YAML文档:
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
YAML列表用[index]解引用器表示为属性键。例如,考虑以下YAML:
my:
servers:
- dev.example.com
- another.example.com
前面的示例将转换为以下属性:
my.servers[0]=dev.example.com
my.servers[1]=another.example.com
像使用Spring Boot的Binder实用程序(正如@ConfigurationProperties所做的)那样,绑定到类似的属性,您需要在类型为java.util.List(或Set)的目标bean中拥有一个属性,或者您需要提供一个setter或使用可变值对其进行初始化。例如,以下示例绑定到前面显示的属性:
@ConfigurationProperties(prefix="my")
public class Config {
private List<String> servers = new ArrayList<String>();
public List<String> getServers() {
return this.servers;
}
}
2.7.2. 在Spring环境中将YAML公开为属性
YamlPropertySourceLoader类可用于在Spring环境中将YAML公开为PropertySource。这样可以让您使用带有占位符语法的@Value批注来访问YAML属性。
2.7.3. 多配置文件YAML文档
您可以使用一个spring.profiles
键来指示文档何时适用,从而在一个文件中指定多个特定于配置文件的YAML文档,如以下示例所示:
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
和 eu-central
配置文件处于活动状态,则server.address
属性为192.168.1.120
。如果development
,production
并eu-central
在配置文件没有启用,那么该属性的值192.168.1.100
。
spring.profiles
因此可以包含一个简单的配置文件名称(例如production
)或一个配置文件表达式。简档表达式允许例如表达更复杂的简档逻辑production & (eu-central | eu-west)
。有关更多详细信息,请参阅参考指南。
果在启动应用程序上下文时未显式激活任何活动,则会激活默认配置文件。因此,在以下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
元素指定的弹簧轮廓可以选择使用!
字符来否定。如果为单个文档指定了否定配置文件和非否定配置文件,则至少一个非否定配置文件必须匹配,并且否定配置文件不能匹配。
2.7.4. YAML 的缺点
YAML files cannot be loaded by using the @PropertySource
annotation. So, in the case that you need to load values that way, you need to use a properties file.
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:
无法使用@PropertySource
注释加载YAML文件。因此,在需要以这种方式加载值的情况下,需要使用属性文件。
在特定于配置文件的YAML文件中使用多YAML文档语法可能会导致意外行为。例如,考虑文件中的以下配置:
server:
port: 8000
---
spring:
profiles: "!test"
security:
user:
password: "secret"
如果使用--spring.profiles.active=dev
参数运行应用程序,则可能希望security.user.password
将其设置为“secret”,但实际情况并非如此。
嵌套文档将被过滤,因为主文件名为application-dev.yml
。它已经被认为是特定于配置文件的,并且嵌套文档将被忽略。
我们建议您不要混用特定于配置文件的YAML文件和多个YAML文档。坚持只使用其中之一。
2.8. 类型安全的配置属性
使用@Value("${property}")
注释注入配置属性有时会很麻烦,尤其是当您使用多个属性或数据本质上是分层的时。Spring Boot提供了一种使用属性的替代方法,该方法使强类型的Bean可以管理和验证应用程序的配置。
请查阅differences between @Value and type-safe configuration properties.
2.8.1. JavaBean 属性绑定
可以绑定一个声明标准JavaBean属性的bean,如以下示例所示:
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) { ... }
}
}
前面的POJO定义了以下属性:
-
acme.enabled
, 默认值为false
. -
acme.remote-address
, 其类型可以从强制转换String
。 -
acme.security.username
, 其嵌套的“安全”对象的名称由属性的名称确定。特别是,返回类型根本不使用,可能已经使用过SecurityProperties
。 -
acme.security.password
. -
acme.security.roles
,String集合,
其默认值为USER
映射到Spring Boot中可用的@ConfigurationProperties类的属性通过properties 文件,YAML文件,环境变量等进行配置,它们是公共API,但是该类本身的访问器(获取器/设置器)不能直接使用。
这种安排依赖于默认的空构造函数,并且getter和setter通常是强制性的,因为绑定是通过标准Java Beans属性描述符进行的,就像在Spring MVC中一样。在以下情况下,可以省略设置器:
- 只要对地图进行了初始化,它们就需要使用获取器,但不一定需要使用设置器,因为它们可以被binder改变。
- 可以通过索引(通常使用YAML)或使用单个逗号分隔的值(属性)来访问集合和数组。在后一种情况下,必须使用二传手。我们建议始终为此类类型添加设置器。如果初始化集合,请确保它不是不可变的(如上例所示)。
- 如果嵌套的POJO属性已初始化(例如上例中的Security字段),则不需要setter。如果希望binder 通过使用其默认构造函数动态创建实例,则需要一个setter。
有些人使用Lombok项目自动添加获取器和设置器。确保Lombok不会为这种类型生成任何特定的构造函数,因为容器会自动使用它来实例化该对象。最后,仅考虑标准Java Bean属性,并且不支持对静态属性的绑定。
2.8.2. 构造函数绑定
上一节中的示例可以以不变的方式重写,如下例所示:
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.bind.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
(例如上面的Security
示例)也将通过其构造函数进行绑定。
可以使用指定默认值,并且将使用@DefaultValue
相同的转换服务将String
值强制为缺少属性的目标类型。
要使用构造函数绑定,必须使用
@EnableConfigurationProperties
或配置属性扫描来启用该类。您无法将构造函数绑定与常规Spring机制创建的bean一起使用(例如@Component的
beans,通过@Bean
方法创建的bean或使用@Import
加载的bean )
如果您的类有多个构造函数,则还可以直接在应绑定的构造函数上使用@ConstructorBinding。
2.8.3. 启用@ConfigurationProperties
注解的类型
Spring Boot提供了绑定@ConfigurationProperties
类型并将其注册为Bean的基础架构。您可以逐类启用配置属性,也可以启用与组件扫描类似的方式进行配置属性扫描。
有时,带@ConfigurationProperties
注解的类可能不适合扫描,例如,如果您正在开发自己的自动配置,或者想要有条件地启用它们。在这些情况下,请使用@EnableConfigurationProperties
注解指定要处理的类型列表。可以在任何@Configuration
类上完成此操作,如以下示例所示:
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(AcmeProperties.class)
public class MyConfiguration {
}
要使用配置属性扫描,请在应用程序中添加@ConfigurationPropertiesScan注解。通常,它将@SpringBootApplication注解添加到使用的主应用程序类中,但可以将其添加到任何@Configuration类中。默认情况下,将从声明注解的类的包中进行扫描。如果要定义要扫描的特定程序包,可以按照以下示例所示进行操作:
@SpringBootApplication
@ConfigurationPropertiesScan({ "com.example.app", "org.acme.another" })
public class MyApplication {
}
使用配置属性扫描或通过@EnableConfigurationProperties注册@ConfigurationProperties Bean时,该Bean具有常规名称:<prefix>-<fqn>,其中<prefix>是@ConfigurationProperties注释和<fqn>中指定的环境密钥前缀。是Bean的完全限定名称。如果注释不提供任何前缀,则仅使用Bean的完全限定名称。上例中的Bean名称为acme-com.example.AcmeProperties。
我们建议@ConfigurationProperties仅处理环境,尤其不要从上下文中注入其他bean。对于极端情况,可以使用setter注入或框架提供的任何* Aware接口(例如,如果需要访问环境,则可以使用例如EnvironmentAware)。如果仍然要使用构造函数注入其他bean,则必须使用@Component注解配置属性bean,并使用基于JavaBean的属性绑定。
2.8.4. 使用带@ConfigurationProperties
注解的类型
这种配置样式特别适合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());
// ...
}
}
使用 @ConfigurationProperties 还可以让您生成元数据文件,IDE 可以使用这些文件为您自己的密钥提供自动完成功能。有关详细信息,请参阅附录。 |
2.8.5. 第三方配置
除了使用 @ConfigurationProperties 来注释类之外,您还可以在公共 @Bean 方法上使用它。当您想要将属性绑定到您无法控制的第三方组件时,这样做会特别有用。
要从环境属性配置 bean,请将@ConfigurationProperties 添加到其 bean 注册中,如以下示例所示:
@ConfigurationProperties(prefix = "another")
@Bean
public AnotherComponent anotherComponent() {
...
}
使用另一个前缀定义的任何 JavaBean 属性都以类似于前面的 AcmeProperties 示例的方式映射到该另一个组件 bean。
2.8.6. Relaxed Binding
Spring Boot 使用一些宽松的规则将环境属性绑定到 @ConfigurationProperties bean,因此环境属性名称和 bean 属性名称之间不需要完全匹配。这很有用的常见示例包括用破折号分隔的环境属性(例如,上下文路径绑定到上下文路径)和大写环境属性(例如,端口绑定到端口)。
例如,请考虑以下 @ConfigurationProperties 类:
@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;
}
}
使用上述代码,可以使用以下属性名称:
属性 | 说明 |
---|---|
| Kebab 案例,建议在 .properties 和 .yml 文件中使用。 |
| 标准驼峰式大小写语法。 |
| 下划线表示法,这是在 .properties 和 .yml 文件中使用的另一种格式。 |
| 大写格式,在使用系统环境变量时推荐。 |
注释的前缀值必须是 kebab 大小写(小写并用 - 分隔,例如 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 | Upper case format with underscore as the delimiter. | 下划线包围的数值,例如 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
上述属性将绑定到以 /key1、/key2 和 key3 作为映射中的键的映射。
对于 YAML 文件,括号需要用引号括起来才能正确解析键。 |
2.8.7. 合并复杂类型
当列表配置在多个位置时,覆盖通过替换整个列表来工作。
例如,假设 MyPojo 对象的名称和描述属性默认为空。以下示例定义了来自 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
如果开发配置文件未激活,则 AcmeProperties.list 包含一个 MyPojo 条目,如前所述。但是,如果启用了开发配置文件,则列表仍然只包含一个条目(名称为我的另一个名称,描述为 null)。此配置不会将第二个 MyPojo 实例添加到列表中,也不会合并项目。
当在多个配置文件中指定一个列表时,将使用具有最高优先级的一个(并且仅使用那个)。考虑以下示例:
acme:
list:
- name: my name
description: my description
- name: another name
description: another description
---
spring:
profiles: dev
acme:
list:
- name: my another name
在前面的例子中,如果dev profile是激活的,AcmeProperties.list包含一个MyPojo条目(名字是my another name
,描述是null)。对于YAML,逗号分隔的列表和YAML列表都可以用来完全覆盖列表的内容。
对于Map属性,你可以用从多个来源抽取的属性值进行绑定。然而,对于多个来源中的同一属性,使用具有最高优先级的那个。下面的例子从AcmeProperties暴露了一个Map<String, MyPojo>。
@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
If the dev
profile is not active, AcmeProperties.map
contains one entry with key key1
(with a name of my name 1
and a description of my description 1
). If the dev
profile is enabled, however, map
contains two entries with keys key1
(with a name of dev name 1
and a description of my description 1
) and key2
(with a name of dev name 2
and a description of 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, description 为 dev description 2) .
上述合并规则适用于所有属性源的属性,而不仅仅是 YAML 文件。 |
2.8.8. 属性转换
Spring Boot attempts to coerce the external application properties to the right type when it binds to the @ConfigurationProperties
beans. If you need custom type conversion, you can provide a ConversionService
bean (with a bean named conversionService
) or custom property editors (through a CustomEditorConfigurer
bean) or custom Converters
(with bean definitions annotated as @ConfigurationPropertiesBinding
).
当 Spring Boot 绑定到 @ConfigurationProperties bean 时,它会尝试将外部应用程序属性强制转换为正确的类型。如果您需要自定义类型转换,您可以提供一个ConversionService bean(带有一个名为conversionService 的bean)或自定义属性编辑器(通过CustomEditorConfigurer bean)或自定义Converters(带有注释为@ConfigurationPropertiesBinding 的bean 定义)。
由于此 bean 在应用程序生命周期的早期就被请求,因此请确保限制您的ConversionService 使用的依赖项。通常,您需要的任何依赖项可能不会在创建时完全初始化。如果配置键强制不需要它并且仅依赖于具有 @ConfigurationPropertiesBinding 限定的自定义转换器,您可能需要重命名您的自定义转换服务。 |
转换工期
Spring Boot 专门支持表达持续时间。如果您公开 java.time.Duration 属性,则应用程序属性中的以下格式可用:
-
常规长整型表示(使用毫秒作为默认单位,除非已指定 @DurationUnit)
-
java.time.Duration 使用的标准 ISO-8601 格式
-
值和单位耦合的更易读的格式(例如,10s 表示 10 秒)
考虑以下示例:
@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;
}
}
要指定 30 秒的会话超时,30、PT30S 和 30s 都是等效的。可以使用以下任何形式指定 500 毫秒的读取超时:500、PT0.5S 和 500 毫秒。
您还可以使用任何受支持的单位。这些是:
-
ns
for nanoseconds -
us
for microseconds -
ms
for milliseconds -
s
for seconds -
m
for minutes -
h
for hours -
d
for days
默认单位是毫秒,可以使用@DurationUnit 覆盖,如上面的示例所示。
如果您从仅使用 Long 表示持续时间的先前版本升级,请确保定义单位(使用 @DurationUnit),如果它不是毫秒,则切换到 Duration。这样做提供了一个透明的升级路径,同时支持更丰富的格式。 |
转换数据大小
Spring Framework 有一个 DataSize 值类型,它以字节为单位表示大小。如果您公开 DataSize 属性,则应用程序属性中的以下格式可用:
-
常规长整型表示(使用字节作为默认单位,除非已指定 @DataSizeUnit)
-
值和单位耦合的更易读的格式(例如,10MB 表示 10 兆字节)
考虑以下示例:
@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;
}
}
要指定 10 MB 的缓冲区大小,10 和 10MB 是等效的。可以将 256 字节的大小阈值指定为 256 或 256B。
您还可以使用任何受支持的单位。这些是:
-
B
for bytes -
KB
for kilobytes -
MB
for megabytes -
GB
for gigabytes -
TB
for terabytes
默认单位是字节,可以使用@DataSizeUnit 覆盖,如上面的示例所示。
如果您要从仅使用 Long 表示大小的先前版本升级,请确保定义单位(使用 @DataSizeUnit)如果它不是字节旁边的切换到 DataSize。这样做提供了一个透明的升级路径,同时支持更丰富的格式。 |
2.8.9. @ConfigurationProperties 校验
每当使用 Spring 的 @Validated 注释进行注释时,Spring Boot 都会尝试验证 @ConfigurationProperties 类。您可以直接在配置类上使用 JSR-303 javax.validation 约束注解。为此,请确保您的类路径中存在兼容的 JSR-303 实现,然后将约束注释添加到您的字段,如以下示例所示:
@ConfigurationProperties(prefix="acme")
@Validated
public class AcmeProperties {
@NotNull
private InetAddress remoteAddress;
// ... getters and setters
}
您还可以通过使用 @Validated 注释创建配置属性的 @Bean 方法来触发验证 |
为确保始终为嵌套属性触发验证,即使未找到任何属性,也必须使用 @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 方法声明为静态可以创建 bean,而无需实例化 @Configuration 类。这样做可以避免早期实例化可能导致的任何问题。
spring-boot-actuator 模块包括一个公开所有 @ConfigurationProperties bean 的端点。将您的网络浏览器指向 /actuator/configprops 或使用等效的 JMX 端点。有关详细信息,请参阅“生产就绪功能”部分。 |
2.8.10. @ConfigurationProperties vs. @Value
@Value 注解是核心容器功能,它不提供与类型安全配置属性相同的功能。下表总结了@ConfigurationProperties 和@Value 支持的功能:
Feature | @ConfigurationProperties | @Value |
---|---|---|
Yes | No | |
Yes | No | |
| No | Yes |
如果您为自己的组件定义了一组配置键,我们建议您将它们分组到使用 @ConfigurationProperties 注释的 POJO 中。您还应该注意,由于 @Value 不支持宽松绑定,因此如果您需要使用环境变量提供值,则它不是一个好的选择。
最后,虽然您可以在 @Value 中编写 SpEL 表达式,但此类表达式不会从应用程序属性文件中处理。