外部化配置
Spring Boot 让你能够外部化配置,以便在不同的环境中使用相同的应用代码。 你可以使用多种外部配置来源,包括 Java 属性文件、YAML 文件、环境变量以及命令行参数。
属性值可以直接使用 @Value 注解注入到您的 Bean 中,通过 Spring 的 Environment 抽象进行访问,或者通过 @ConfigurationProperties 绑定到结构化对象。
Spring Boot 使用一种非常特定的 PropertySource 顺序,旨在允许合理地覆盖值。
后面的属性源可以覆盖前面定义的属性值。
_sources_ 被考虑的顺序为:
-
默认属性(通过设置
SpringApplication.setDefaultProperties(Map)指定)。 -
@PropertySource注解应用于您的@Configuration类。 请注意,此类属性源直到应用程序上下文刷新时才会添加到Environment中。 此时配置某些属性(如logging.*和spring.main.*)为时已晚,因为这些属性在刷新开始之前就被读取了。 -
配置数据(例如
application.properties文件)。 -
一个仅在
random.*中具有属性的RandomValuePropertySource。 -
操作系统环境变量。
-
Java 系统属性 (
System.getProperties())。 -
来自
java:comp/env的JNDI属性。 -
ServletContext初始化参数。 -
ServletConfig初始化参数。 -
来自
SPRING_APPLICATION_JSON的属性(嵌入在环境变量或系统属性中的 inline JSON)。 -
命令行参数。
-
properties属性应用于您的测试。 可在@SpringBootTest以及用于测试应用程序特定切片的 测试注解 上使用。 -
@DynamicPropertySource在您的测试中使用注解。 -
@TestPropertySource在您的测试上使用注解。 -
Devtools 全局设置属性 在 devtools 激活时位于
$HOME/.config/spring-boot目录中。
配置数据文件按照以下顺序考虑:
-
应用程序属性 打包在您的 jar 文件中(
application.properties及其 YAML 变体)。 -
基于配置文件的应用属性被打包在你的jar文件中(
application-{profile}.properties及其YAML变体)。 -
外部配置文件(如
application.properties及其YAML变体),位于打包的jar文件之外。 -
基于配置文件的应用属性 位于你的打包jar外部(
application-{profile}.properties和 YAML 变体)。
建议整个应用程序采用一种格式。
如果在同一位置既有.properties格式又有YAML格式的配置文件,.properties格式优先级更高。 |
如果你使用环境变量而非系统属性,大多数操作系统不允许使用带点号(.)分隔的键名,但你可以改用下划线(例如,使用 SPRING_CONFIG_NAME 而不是 spring.config.name)。
详情请参见从环境变量绑定。 |
如果您的应用程序运行在Servlet容器或应用服务器中,可以使用JNDI属性(位于java:comp/env)或Servlet上下文初始化参数,替代或者同时使用环境变量或系统属性。 |
为了提供一个具体的示例,假设您开发了一个使用 name 属性的 @Component,如下例所示:
-
Java
-
Kotlin
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class MyBean {
@Value("${name}")
private String name;
// ...
}
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Component
@Component
class MyBean {
@Value("\${name}")
private val name: String? = null
// ...
}
在您的应用程序类路径中(例如,在您的 jar 包内),可以有一个 application.properties 文件,它提供了一个合理的默认属性值用于 name。
当在新的环境中运行时,可以在 jar 包外提供一个 application.properties 文件以覆盖 name 的值。
对于一次性测试,可以使用特定的命令行参数启动应用程序(例如,java -jar app.jar --name="Spring")。
env 和 configprops 端点在确定某个属性为何具有特定值时非常有用。
您可以使用这两个端点来诊断属性值异常的问题。
详情请参阅生产就绪功能部分。 |
访问命令行属性
默认情况下,SpringApplication 会将任何命令行选项参数(即以 -- 开头的参数,例如 --server.port=9000)转换为 property,并将其添加到 Spring Environment 中。
如前所述,命令行属性始终优先于基于文件的属性源。
如果您不希望将命令行属性添加到 Environment,您可以使用 SpringApplication.setAddCommandLineProperties(false) 禁用它们。
JSON 应用程序属性
环境变量和系统属性有时会有限制,这意味着某些属性名称不能使用。 为了解决这个问题,Spring Boot 允许将一组属性编码成单一的 JSON 结构。
当您的应用程序启动时,任何 spring.application.json 或 SPRING_APPLICATION_JSON 属性将被解析并添加到 Environment。
例如,可以在 UN*X 壳中作为环境变量在命令行中提供 SPRING_APPLICATION_JSON 属性:
$ SPRING_APPLICATION_JSON='{"my":{"name":"test"}}' java -jar myapp.jar
在前面的示例中,您最终会在 Spring Environment 中得到 my.name=test。
同样地,也可以将相同的JSON作为系统属性提供:
$ java -Dspring.application.json='{"my":{"name":"test"}}' -jar myapp.jar
或者您可以使用命令行参数提供JSON:
$ java -jar myapp.jar --spring.application.json='{"my":{"name":"test"}}'
如果您部署到经典应用服务器,也可以使用名为java:comp/env/spring.application.json的JNDI变量。
尽管来自 JSON 的 null 值会被添加到生成的属性源中,但 PropertySourcesPropertyResolver 将 null 属性视为缺失值。
这意味着该 JSON 无法使用 null 值覆盖来自较低优先级属性源的属性。 |
外部应用程序属性
Spring Boot将在您的应用程序启动时自动查找并加载以下位置的application.properties和application.yaml文件:
-
从类路径
-
类路径根目录
-
类路径
/config包
-
-
从当前目录
-
当前目录
-
当前目录下的
config/子目录 -
config/子目录下的直接子目录
-
该列表按优先级排序(后一项的值将覆盖前一项)。
从加载的文件中提取的文档会作为 PropertySource 实例添加到 Spring Environment 中。
如果您不喜欢将配置文件命名为application,可以通过指定spring.config.name环境属性来切换到另一个文件名。
例如,要查找myproject.properties和myproject.yaml文件,您可以按照以下方式运行应用程序:
$ java -jar myproject.jar --spring.config.name=myproject
您也可以通过使用spring.config.location环境属性来引用显式位置。
此属性接受一个或多个位置的逗号分隔列表,用于检查。
以下示例展示了如何指定两个不同的文件:
$ java -jar myproject.jar --spring.config.location=\
optional:classpath:/default.properties,\
optional:classpath:/override.properties
如果位置是可选的,并且你不介意它们不存在,请使用前缀 #features.external-config.files.optional-prefix。 |
spring.config.name, spring.config.location, and spring.config.additional-location 用于早期确定需要加载哪些文件。
它们必须定义为环境属性(通常是操作系统环境变量、系统属性或命令行参数)。 |
如果 `spring.config.location` 包含目录(而非文件),这些目录应以 `/` 结尾。 在运行时,它们将与从 `spring.config.name` 生成的名称进行拼接后再被加载。 直接指定在 `spring.config.location` 中的文件会被导入。
Both directory and file location values are also expanded to check for 特定配置文件的配置项。
For example, if you have a spring.config.location of classpath:myconfig.properties, appropriate classpath:myconfig-<profile>.properties files will also be loaded. |
在大多数情况下,您添加的每个spring.config.location项都将引用一个单独的文件或目录。
位置将按照定义的顺序进行处理,并且后来的项可以覆盖早期项的值。
如果你的配置位置设置较为复杂,并且使用了特定于 profile 的配置文件,你可能需要提供额外的提示,以便 Spring Boot 知道应如何对它们进行分组。
位置组(location group)是一组处于同一层级的配置位置的集合。
例如,你可能希望将所有 classpath 下的位置归为一组,再将所有外部位置归为另一组。
位置组内的各项应使用 ; 分隔。
更多详细信息,请参见特定于 Profile 的文件部分中的示例。
使用spring.config.location配置的位置会替换默认位置。
例如,如果spring.config.location配置的值为optional:classpath:/custom-config/,optional:file:./custom-config/,那么考虑的完整位置集为:
-
optional:classpath:custom-config/ -
optional:file:./custom-config/
如果希望添加额外的配置位置而不是替换它们,可以使用spring.config.additional-location。
从额外位置加载的属性可以覆盖默认位置中的属性。
例如,如果spring.config.additional-location配置为值optional:classpath:/custom-config/,optional:file:./custom-config/,则考虑的所有配置位置集为:
-
optional:classpath:/;optional:classpath:/config/ -
optional:file:./;optional:file:./config/;optional:file:./config/*/ -
optional:classpath:custom-config/ -
optional:file:./custom-config/
此搜索顺序让您可以在一个配置文件中指定默认值,然后在另一个文件中选择性地覆盖这些值。
您可以在 application.properties(或其他通过 spring.config.name 选择的 basename)之一的默认位置为应用程序提供默认值。
这些默认值可以随后在自定义位置中的不同文件中运行时被覆盖。
可选位置
默认情况下,当指定的配置数据位置不存在时,Spring Boot 将抛出 ConfigDataLocationNotFoundException,并且您的应用程序将无法启动。
如果您想指定一个位置,但不介意它是否始终存在,可以使用 optional: 前缀。
您可以将此前缀与 spring.config.location 和 spring.config.additional-location 属性一起使用,也可以与 spring.config.import 声明一起使用。
例如,spring.config.import 值为 optional:file:./myconfig.properties 允许您的应用程序即使缺少 myconfig.properties 文件也能启动。
如果您希望忽略所有 ConfigDataLocationNotFoundException 错误并始终继续启动应用程序,可以使用 spring.config.on-not-found 属性。
通过 SpringApplication.setDefaultProperties(…) 或使用系统/环境变量将值设置为 ignore。
通配符位置
如果配置文件位置的最后一个路径段包含 * 字符,那么它被视为通配符位置。
在加载配置时会扩展通配符,因此还会检查即时子目录。
通配符位置特别适用于像 Kubernetes 这样的环境中,当有多个配置属性来源时尤为有用。
例如,假设你有一些 Redis 配置和一些 MySQL 配置,你可能希望将这两部分配置分开,同时要求它们都包含在一个 application.properties 文件中。
这可能会导致两个单独的 application.properties 文件分别挂载在不同的位置,例如 /config/redis/application.properties 和 /config/mysql/application.properties。
在这种情况下,使用通配符路径 config/*/ 将会使得这两个文件都被处理。
默认情况下,Spring Boot 包含 config/*/ 在默认搜索位置中。
这意味着位于你的 jar 之外的所有 /config 目录的子目录都会被搜索。
您可以使用通配符位置自行配置spring.config.location和spring.config.additional-location属性。
通配符位置只能包含一个 * 并且必须以 */ 结束,用于目录的搜索位置或以 */<filename> 结尾,用于文件的搜索位置。
带有通配符的位置根据文件名的绝对路径进行字母顺序排序。 |
通配符路径仅适用于外部目录。
您不能在classpath:路径中使用通配符。 |
特定于配置文件的文件
除了 application 属性文件,Spring Boot 还会尝试使用命名惯例 application-{profile} 加载特定配置文件。
例如,如果您的应用程序激活了一个名为 prod 的配置文件并且使用 YAML 文件,则同时会考虑 application.yaml 和 application-prod.yaml 这两个文件。
特性特定的属性从与标准application.properties相同的位置加载,特性特定的文件总是覆盖非特定的文件。
如果指定了多个特性,采用最后获胜策略。
例如,如果通过prod,live属性指定了特性spring.profiles.active,那么application-prod.properties中的值可以被application-live.properties中的值覆盖。
|
last-wins 策略在 位置组 水平上适用。
一个 例如,继续我们上面的 /cfg application-live.properties /ext application-live.properties application-prod.properties 当我们的
当我们将路径设置为
|
Environment 拥有一组默认配置文件(默认为 [default]),在未设置任何活动配置文件时使用。
换句话说,如果未显式激活任何配置文件,则将考虑来自 application-default 的属性。
| 属性文件仅加载一次。 如果已经直接 导入 了特定配置文件的属性文件,则不会再次导入。 |
导入额外数据
应用属性可以通过spring.config.import属性从其他位置导入进一步的配置数据。
导入会在被发现时进行处理,并被视为在声明导入的那个文档下方立即插入的额外文档。
在类路径中的application.properties文件中,你可能会有如下内容:
-
Properties
-
YAML
spring.application.name=myapp
spring.config.import=optional:file:./dev.properties
spring:
application:
name: "myapp"
config:
import: "optional:file:./dev.properties"
这将会触发当前目录下的dev.properties文件的导入(如果该文件存在)。
从导入的dev.properties文件中获取的值将优先于触发导入的文件中的值。
在上述示例中,dev.properties可以重新定义spring.application.name为不同的值。
一个导入无论被声明多少次都只会被导入一次。
使用“固定”和“相对导入”位置
Imports may be specified as 固定或导入相对路径。
固定位置始终会解析为同一个基础资源,无论spring.config.import属性在哪里声明。
导入相对路径则相对于声明spring.config.import属性的文件进行解析。
以斜杠(/)开头或带有 URL 样式的前缀(如 file:, classpath: 等)的位置被视为固定位置。
所有其他位置则被视为导入相对路径。
optional:前缀在确定位置是否为固定或导入相对时不予考虑。 |
作为示例,假设我们有一个 /demo 目录包含我们的 application.jar 文件。
我们可能会在该目录下添加一个 /demo/application.properties 文件,并填写以下内容:
spring.config.import=optional:core/core.properties
这是一相对路径,并且会尝试加载文件/demo/core/core.properties,如果该文件存在。
如果/demo/core/core.properties包含以下内容:
spring.config.import=optional:extra/extra.properties
它将尝试加载/demo/core/extra/extra.properties。
optional:extra/extra.properties相对于/demo/core/core.properties,因此完整的目录是/demo/core/ + extra/extra.properties。
属性排序
内部定义的导入顺序并不重要,无需在意其在 properties/yaml 文件中的位置。例如,下面两个示例会产生相同的结果:
-
Properties
-
YAML
spring.config.import=my.properties
my.property=value
spring:
config:
import: "my.properties"
my:
property: "value"
-
Properties
-
YAML
my.property=value
spring.config.import=my.properties
my:
property: "value"
spring:
config:
import: "my.properties"
在上述两个示例中,my.properties 文件中的值将优先于触发其导入的文件。
在单个spring.config.import键下可以指定多个位置。
这些位置将按照定义的顺序进行处理,后期导入的位置优先级更高。
当合适时,特定配置文件的变体也会被考虑导入。
上述示例将导入 my.properties 以及任何 my-<profile>.properties 变体。 |
|
Spring Boot 包含可插拔的 API,允许支持各种不同的位置地址。 默认情况下,您可以导入 Java Properties、YAML 和配置树。 第三方 JAR 文件可以提供对其他技术的支持(文件不一定需要本地存在)。 例如,你可以想象配置数据来自于外部存储,如 Consul、Apache ZooKeeper 或 Netflix Archaius。 如果您想支持自己的位置,请参阅 |
导入无扩展名的文件
一些云平台无法为挂载的文件添加文件扩展名。 为了导入这些无扩展名的文件,您需要给 Spring Boot 提供一个提示,以便它知道如何加载这些文件。 您可以这样做,在方括号中放置一个扩展名提示。
例如,假设你有一个/etc/config/myconfig文件,希望将其导入为yaml格式。
你可以通过以下方式从application.properties中导入:
-
Properties
-
YAML
spring.config.import=file:/etc/config/myconfig[.yaml]
spring:
config:
import: "file:/etc/config/myconfig[.yaml]"
使用环境变量
当在云平台(如 Kubernetes)上运行应用程序时,您通常需要读取平台提供的配置值。 您可以使用环境变量来实现这一目的,或者可以使用配置树。
您甚至可以将整个配置存储在环境变量中的属性或 yaml 格式(多行)中,并使用 env: 前缀加载它们。
假设有一个名为 MY_CONFIGURATION 的环境变量,其内容如下:
my.name=Service1
my.cluster=Cluster1
使用 env: 前缀可以导入此变量中的所有属性:
-
Properties
-
YAML
spring.config.import=env:MY_CONFIGURATION
spring:
config:
import: "env:MY_CONFIGURATION"
此功能还支持指定扩展名。
默认扩展名为.properties。 |
使用配置树
将配置值存储在环境变量中存在缺点,特别是在该值需要保密的情况下。
作为环境变量的一种替代方案,许多云平台现在允许您将配置映射到挂载的数据卷中。
例如,Kubernetes 可以同时卷挂载 ConfigMaps 和 Secrets。
存在两种常见的卷挂载模式可以使用:
-
单个文件包含完整的属性集(通常以 YAML 格式书写)。
-
多个文件被写入到目录树中,文件名成为‘键’,文件内容成为‘值’。
对于第一种情况,你可以直接使用spring.config.import导入YAML或Properties文件,如上述所述。
对于第二种情况,你需要使用configtree:前缀,以便Spring Boot知道需要将所有文件暴露为属性。
作为示例,让我们假设Kubernetes挂载了以下卷:
etc/
config/
myapp/
username
password
username 文件的内容将是配置值,而 password 的内容将是一个密钥。
要导入这些属性,您可以在application.properties或application.yaml文件中添加以下内容:
-
Properties
-
YAML
spring.config.import=optional:configtree:/etc/config/
spring:
config:
import: "optional:configtree:/etc/config/"
然后,您可以按常规方式从 Environment 中访问或注入 myapp.username 和 myapp.password 属性。
The names of the folders and files under the config tree form the property name.
In the above example, to access the properties as username and password, you can set spring.config.import to optional:configtree:/etc/config/myapp. |
使用点表示法的文件名也能被正确映射。
例如,在上面的示例中,/etc/config 中名为 myapp.username 的文件将在 Environment 中生成一个 myapp.username 属性。 |
配置树值可以根据预期的内容绑定到字符串 String 和 byte[] 类型。 |
如果需要从同一个父文件夹导入多个配置树,可以使用通配符快捷方式。
任何以configtree:结尾并带有/*/的配置树位置将导入所有直接子项作为配置树。
这与非通配符导入相同,在每个配置树下的文件夹和文件名称组成了属性名。
例如,给定以下volume:
etc/
config/
dbconfig/
db/
username
password
mqconfig/
mq/
username
password
您可以通过configtree:/etc/config/*/作为导入位置:
-
Properties
-
YAML
spring.config.import=optional:configtree:/etc/config/*/
spring:
config:
import: "optional:configtree:/etc/config/*/"
这将会添加db.username, db.password, mq.username 和 mq.password 属性。
| 使用通配符加载的目录会按字母顺序排序。 如果需要不同的顺序,请将每个位置单独列出作为导入。 |
配置树也可以用于 Docker 密钥。
当一个 Docker swarm 服务被授予访问某个密钥的权限时,该密钥会被挂载到容器中。
例如,如果一个名为 db.password 的密钥在位置 /run/secrets/ 被挂载,你可以通过以下方式将 db.password 提供给 Spring 环境:
-
Properties
-
YAML
spring.config.import=optional:configtree:/run/secrets/
spring:
config:
import: "optional:configtree:/run/secrets/"
属性占位符
application.properties 和 application.yaml 中的值在使用时会通过现有的 Environment 进行过滤,因此您可以引用先前定义的值(例如,来自系统属性或环境变量)。
标准的 ${name} 属性占位符语法可以在值内的任何地方使用。
属性占位符还可以使用 : 来指定默认值,以将默认值与属性名分隔开,例如 ${name:default}。
使用占位符(带默认值和不带默认值)的例子如下所示:<br/>
-
Properties
-
YAML
app.name=MyApp
app.description=${app.name} is a Spring Boot application written by ${username:Unknown}
app:
name: "MyApp"
description: "${app.name} is a Spring Boot application written by ${username:Unknown}"
假设没有在其他地方设置username属性,app.description将具有值MyApp is a Spring Boot application written by Unknown。
|
您应始终使用规范形式(仅使用小写字母的短横线命名法)在占位符中引用属性名称。
这将允许 Spring Boot 使用与 宽松绑定 例如, |
| 你也可以使用这种技术为现有的 Spring Boot 属性创建“简短”变体。 有关详细信息,请参阅“How-to 指南”中的使用‘简短’命令行参数一节。 |
处理多文档文件
Spring Boot 允许你将单个物理文件拆分成多个逻辑文档,每个文档可以独立添加。 文档按顺序处理,从上到下依次进行。 后来的文档可以在先前定义的属性上覆盖这些属性。
对于application.yaml文件,使用标准的YAML多文档语法。
三个连续的破折号代表一个文档的结束,并且是下一个文档的开始。
例如,以下文件包含两个逻辑文档:
spring:
application:
name: "MyApp"
---
spring:
application:
name: "MyCloudApp"
config:
activate:
on-cloud-platform: "kubernetes"
对于 `application.properties` 文件,使用特殊的注释标记 `application.properties` 或 `#---` 来分隔文档:
spring.application.name=MyApp
#---
spring.application.name=MyCloudApp
spring.config.activate.on-cloud-platform=kubernetes
| 属性文件分隔符不应有任何前置空格,并且必须恰好包含三个连字符。 分隔符前面和后面的行不应具有相同的注释前缀。 |
多文档属性文件通常与激活属性(例如 spring.config.activate.on-profile)结合使用。
详见下一节。 |
无法使用 @PropertySource 或 @TestPropertySource 注解来加载多文档属性文件。 |
激活属性
当满足某些条件时,有时仅激活一组特定属性是有用的。 例如,你可能有一些属性,只有在特定配置文件活动时才相关。
您可以使用spring.config.activate.*条件性地激活一个属性文件。
以下激活属性可用:
| <property> </property> | 注意 |
|---|---|
|
必须匹配的配置文件表达式,或者至少一个需要匹配的配置文件表达式的列表,以使文档生效。 |
|
必须检测到 |
例如,以下内容表示第二个文档仅在运行于Kubernetes时,并且仅在“prod”或“staging”配置文件激活时才生效:
-
Properties
-
YAML
myprop=always-set
#---
spring.config.activate.on-cloud-platform=kubernetes
spring.config.activate.on-profile=prod | staging
myotherprop=sometimes-set
myprop:
"always-set"
---
spring:
config:
activate:
on-cloud-platform: "kubernetes"
on-profile: "prod | staging"
myotherprop: "sometimes-set"
加密属性
Spring Boot 并未提供任何用于加密属性值的内置支持,但它确实提供了必要的钩子点,以便修改包含在 Spring Environment 中的值。
EnvironmentPostProcessor 接口允许您在应用程序启动之前操作 Environment。
有关详细信息,请参阅 在 Environment 或 ApplicationContext 启动前对其进行自定义。
如果你需要一种安全的方式来存储凭证和密码,Spring Cloud Vault 项目提供了将外部化配置存储在 HashiCorp Vault 中的支持。
使用 YAML 工作
YAML 是 JSON 的超集,因此是一种用于指定分层配置数据的便捷格式。
只要类路径中存在 SnakeYAML 库,SpringApplication 类就会自动支持将 YAML 作为 properties 文件的替代方案。
如果使用了Starters,SnakeYAML 将由 spring-boot-starter 自动提供。 |
将 YAML 映射到属性
YAML 文档需要从其层级格式转换为扁平结构,以便与 Spring Environment 一起使用。
例如,考虑以下 YAML 文档:
environments:
dev:
url: "https://dev.example.com"
name: "Developer Setup"
prod:
url: "https://another.example.com"
name: "My Cool App"
为了从 Environment 访问这些属性,它们将被展平如下:
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
likewise, YAML 列表也需要被展平。
它们表示为带有 [index] 解引用器的属性键。
例如,考虑以下 YAML:
my:
servers:
- "dev.example.com"
- "another.example.com"
先前的例子将被转换为这些属性:
my.servers[0]=dev.example.com
my.servers[1]=another.example.com
无法使用 @PropertySource 或 @TestPropertySource 注解来加载 YAML 文件。
因此,如果您需要以这种方式加载值,则必须使用 properties 文件。 |
直接加载 YAML
Spring Framework 提供了两个便捷的类,可用于加载 YAML 文档。
YamlPropertiesFactoryBean 将 YAML 加载为 Properties,而 YamlMapFactoryBean 则将 YAML 加载为 Map。
如果您想将 YAML 加载为 Spring PropertySource,也可以使用 YamlPropertySourceLoader 类。
配置随机值
RandomValuePropertySource 可用于注入随机值(例如,用于密钥或测试用例)。
它可以生成整数、长整型、UUID 或字符串,如下例所示:
-
Properties
-
YAML
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]}
my:
secret: "${random.value}"
number: "${random.int}"
bignumber: "${random.long}"
uuid: "${random.uuid}"
number-less-than-ten: "${random.int(10)}"
number-in-range: "${random.int[1024,65536]}"
The random.int* 语法是 OPEN value (,max) CLOSE,其中 OPEN,CLOSE 是任意字符,value,max 是整数。
如果提供了 max,那么 value 是最小值,max 是最大值(不包含)。
配置系统环境属性
Spring Boot 支持为环境属性设置前缀。
如果系统环境被多个具有不同配置需求的 Spring Boot 应用程序共享,这将非常有用。
系统环境属性的前缀可以直接在 SpringApplication 上通过调用 setEnvironmentPrefix(…) 方法来设置,且需在应用程序运行之前完成。
例如,如果将前缀设置为input,则属性remote.timeout将在系统环境中解析为INPUT_REMOTE_TIMEOUT。
The prefix only applies to system environment properties. The example above would continue to use |
类型安全的配置属性
使用 @Value("${property}") 注解注入配置属性在处理多个属性或数据具有层次结构时可能会有些繁琐。
Spring Boot 提供了一种替代的方法来处理属性,这种方式允许强类型bean控制并验证应用程序的配置。
JavaBean 属性绑定
可以将一个bean绑定声明标准JavaBean属性,例如在以下示例中所示:
-
Java
-
Kotlin
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("my.service")
public class MyProperties {
private boolean enabled;
private InetAddress remoteAddress;
private final Security security = new Security();
// getters / setters...
public boolean isEnabled() {
return this.enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public InetAddress getRemoteAddress() {
return this.remoteAddress;
}
public void setRemoteAddress(InetAddress remoteAddress) {
this.remoteAddress = remoteAddress;
}
public Security getSecurity() {
return this.security;
}
public static class Security {
private String username;
private String password;
private List<String> roles = new ArrayList<>(Collections.singleton("USER"));
// getters / setters...
public String getUsername() {
return this.username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
this.password = password;
}
public List<String> getRoles() {
return this.roles;
}
public void setRoles(List<String> roles) {
this.roles = roles;
}
}
}
import org.springframework.boot.context.properties.ConfigurationProperties
import java.net.InetAddress
@ConfigurationProperties("my.service")
class MyProperties {
var isEnabled = false
var remoteAddress: InetAddress? = null
val security = Security()
class Security {
var username: String? = null
var password: String? = null
var roles: List<String> = ArrayList(setOf("USER"))
}
}
preceding POJO 定义了以下属性:
-
my.service.enabled, 默认值为false。 -
my.service.remote-address,其类型可以从String转换而来。 -
my.service.security.username,带有一个嵌套的"security"对象,其名称由属性名决定。 特别是,此处完全未使用类型,它本可以是SecurityProperties。 -
my.service.security.password. -
my.service.security.roles,带有一个String集合,其默认值为USER。
要在属性名称中使用保留关键字(例如 my.service.import),请在属性的字段上使用 @Name 注解。 |
映射到 Spring Boot 中可用的 @ConfigurationProperties 类的属性是通过属性文件、YAML 文件、环境变量及其他机制进行配置的,这些属性属于公共 API,但该类本身的访问器(getter/setter)并不打算直接使用。 |
|
此类安排依赖于默认的空构造函数,通常还需要提供getter和setter方法,因为绑定是通过标准Java Beans属性描述符进行的,就像在Spring MVC中一样。 在以下情况下可以省略setter方法:
有些人使用 Project Lombok 来自动添加 getter 和 setter 方法。 确保 Lombok 不为此类类型生成任何特定的构造函数,因为容器会自动使用它来实例化对象。 最后,仅考虑标准Java Beans属性,并且绑定静态属性不被支持。 |
构造函数绑定
上一节中的示例可以以不可变的方式重新编写,如下所示:
-
Java
-
Kotlin
import java.net.InetAddress;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.bind.DefaultValue;
@ConfigurationProperties("my.service")
public class MyProperties {
// fields...
private final boolean enabled;
private final InetAddress remoteAddress;
private final Security security;
public MyProperties(boolean enabled, InetAddress remoteAddress, Security security) {
this.enabled = enabled;
this.remoteAddress = remoteAddress;
this.security = security;
}
// getters...
public boolean isEnabled() {
return this.enabled;
}
public InetAddress getRemoteAddress() {
return this.remoteAddress;
}
public Security getSecurity() {
return this.security;
}
public static class Security {
// fields...
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;
}
// getters...
public String getUsername() {
return this.username;
}
public String getPassword() {
return this.password;
}
public List<String> getRoles() {
return this.roles;
}
}
}
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.context.properties.bind.DefaultValue
import java.net.InetAddress
@ConfigurationProperties("my.service")
class MyProperties(val enabled: Boolean, val remoteAddress: InetAddress,
val security: Security) {
class Security(val username: String, val password: String,
@param:DefaultValue("USER") val roles: List<String>)
}
在此配置中,存在单个带参数的构造函数意味着应使用构造函数绑定。
这意味着绑定器将找到具有您希望绑定的参数的构造函数。
如果您的类有多个构造函数,则可以使用 @ConstructorBinding 注解来指定用于构造函数绑定的构造函数。
若要为某个类选择退出构造函数绑定,带参数的构造函数必须使用 @Autowired 进行注解,或声明为 private。
Kotlin 开发者可以使用空的主构造函数来选择退出构造函数绑定。
例如:
-
Java
-
Kotlin
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("my")
public class MyProperties {
// fields...
final MyBean myBean;
private String name;
@Autowired
public MyProperties(MyBean myBean) {
this.myBean = myBean;
}
// getters / setters...
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
import org.springframework.boot.context.properties.ConfigurationProperties
@ConfigurationProperties("my")
class MyProperties() {
constructor(name: String) : this() {
this.name = name
}
// vars...
var name: String? = null
}
构造器绑定可用于记录(record)。
除非您的记录有多个构造器,否则无需使用 @ConstructorBinding。
嵌套在构造函数绑定类中的成员(例如上面示例中的Security)也将通过其构造函数进行绑定。
可以使用 @DefaultValue 在构造函数参数和记录组件上指定默认值。
转换服务将应用于强制转换注解的 String 值,以匹配缺失属性的目标类型。
参考前面的示例,如果没有属性绑定到 Security,则 MyProperties 实例将为 security 包含一个 null 值。
为了使其即使在没有属性绑定到它时(当使用 Kotlin 时,这将要求 Security 的 username 和 password 参数声明为可空,因为它们没有默认值)也包含一个非空的 Security 实例,请使用空的 @DefaultValue 注解:
-
Java
-
Kotlin
public MyProperties(boolean enabled, InetAddress remoteAddress, @DefaultValue Security security) {
this.enabled = enabled;
this.remoteAddress = remoteAddress;
this.security = security;
}
class MyProperties(val enabled: Boolean, val remoteAddress: InetAddress,
@DefaultValue val security: Security) {
class Security(val username: String?, val password: String?,
@param:DefaultValue("USER") val roles: List<String>)
}
要使用构造函数绑定,必须通过 @EnableConfigurationProperties 或配置属性扫描启用该类。
您无法对通过常规 Spring 机制创建的 Bean 使用构造函数绑定(例如 @Component Bean、通过使用 @Bean 方法创建的 Bean 或使用 @Import 加载的 Bean) |
要使用构造函数绑定,类必须使用-parameters进行编译。
这会在你使用Spring Boot的Gradle插件或使用Maven和spring-boot-starter-parent时自动发生。 |
不建议将 Optional 与 @ConfigurationProperties 一起使用,因为它主要设计用作返回类型。
因此,它并不适合用于配置属性的注入。
为了与其他类型的属性保持一致,如果您确实声明了一个 Optional 属性且该属性没有值,则将绑定为 null,而不是一个空的 Optional。 |
要在属性名称中使用保留关键字(例如 my.service.import),请在构造函数参数上使用 @Name 注解。 |
启用带有 @ConfigurationProperties 注解的类型
Spring Boot 提供了基础设施,用于绑定 @ConfigurationProperties 类型并将它们注册为 Bean。
您可以逐个类地启用配置属性,也可以启用配置属性扫描,其工作方式类似于组件扫描。
有时,带有 @ConfigurationProperties 注解的类可能不适合被扫描,例如,当您正在开发自己的自动配置或希望有条件地启用它们时。
在这些情况下,请使用 @EnableConfigurationProperties 注解指定要处理的类型列表。
这可以在任何 @Configuration 类上完成,如下例所示:
-
Java
-
Kotlin
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(SomeProperties.class)
public class MyConfiguration {
}
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.context.annotation.Configuration
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(SomeProperties::class)
class MyConfiguration
-
Java
-
Kotlin
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("some.properties")
public class SomeProperties {
}
import org.springframework.boot.context.properties.ConfigurationProperties
@ConfigurationProperties("some.properties")
class SomeProperties
要使用配置属性扫描,请将 @ConfigurationPropertiesScan 注解添加到您的应用程序中。
通常,它会被添加到使用 @SpringBootApplication 注解的主应用程序类中,但也可以添加到任何 @Configuration 类中。
默认情况下,扫描将从声明该注解的类所在的包开始。
如果您希望定义特定的扫描包,可以按照以下示例进行操作:
-
Java
-
Kotlin
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
@SpringBootApplication
@ConfigurationPropertiesScan({ "com.example.app", "com.example.another" })
public class MyApplication {
}
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.context.properties.ConfigurationPropertiesScan
@SpringBootApplication
@ConfigurationPropertiesScan("com.example.app", "com.example.another")
class MyApplication
|
当使用配置属性扫描或通过 假设在 |
我们建议 @ConfigurationProperties 仅处理环境相关事务,尤其是不应从上下文中注入其他 Bean。
对于特殊情况,可以使用设值注入,或使用框架提供的任意 *Aware 接口(例如,若您需要访问 Environment,则可使用 EnvironmentAware)。
如果您仍希望通过构造函数注入其他 Bean,则配置属性 Bean 必须使用 @Component 进行注解,并采用基于 JavaBean 的属性绑定。
使用带有 @ConfigurationProperties 注解的类型
这种配置方式特别适用于 SpringApplication 外部 YAML 配置,如下例所示:
my:
service:
remote-address: 192.168.1.1
security:
username: "admin"
roles:
- "USER"
- "ADMIN"
要使用 @ConfigurationProperties Bean,您可以像注入任何其他 Bean 一样注入它们,如下例所示:
-
Java
-
Kotlin
import org.springframework.stereotype.Service;
@Service
public class MyService {
private final MyProperties properties;
public MyService(MyProperties properties) {
this.properties = properties;
}
public void openConnection() {
Server server = new Server(this.properties.getRemoteAddress());
server.start();
// ...
}
// ...
}
import org.springframework.stereotype.Service
@Service
class MyService(val properties: MyProperties) {
fun openConnection() {
val server = Server(properties.remoteAddress)
server.start()
// ...
}
// ...
}
使用 @ConfigurationProperties 还可以让您生成元数据文件,IDE 可以利用这些文件为您自定义的配置键提供自动补全功能。
详见 附录。 |
第三方配置
除了使用 @ConfigurationProperties 注解类之外,您还可以将其用于公共 @Bean 方法。
当您希望将属性绑定到不受您控制的第三方组件时,这样做尤其有用。
要从 Environment 属性配置一个 bean,请将其添加到其 bean 注册中,如下例所示:@ConfigurationProperties
-
Java
-
Kotlin
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
public class ThirdPartyConfiguration {
@Bean
@ConfigurationProperties("another")
public AnotherComponent anotherComponent() {
return new AnotherComponent();
}
}
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@Configuration(proxyBeanMethods = false)
class ThirdPartyConfiguration {
@Bean
@ConfigurationProperties("another")
fun anotherComponent(): AnotherComponent = AnotherComponent()
}
任意以another前缀定义的JavaBean属性将类似前面的AnotherComponent示例一样映射到SomeProperties bean中。
松散绑定
Spring Boot 使用一些宽松的规则将 Environment 属性绑定到 @ConfigurationProperties Bean,因此Environment 属性名称与 Bean 属性名称无需完全匹配。
常见的有用示例包括带连字符的环境属性(例如,context-path 绑定到 contextPath)和大写的环境属性(例如,PORT 绑定到 port)。
例如,考虑以下 @ConfigurationProperties 类:
-
Java
-
Kotlin
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("my.main-project.person")
public class MyPersonProperties {
private String firstName;
public String getFirstName() {
return this.firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
}
import org.springframework.boot.context.properties.ConfigurationProperties
@ConfigurationProperties("my.main-project.person")
class MyPersonProperties {
var firstName: String? = null
}
使用上述代码,以下属性名称都可以被使用:
| <property> </property> | 注意 |
|---|---|
|
小驼峰命名法(kebab-case),推荐在 |
|
标准驼峰命名法语法。 |
|
使用下划线表示法(underscore notation),这是一种在.properties和YAML文件中使用的替代格式。 |
|
使用大写格式,这是在使用系统环境变量时推荐的方式。 |
注解的prefix值必须采用 kebab case 格式(小写且通过 - 分隔,例如 my.main-project.person)。 |
| 属性源 | 简单的 | 列表 |
|---|---|---|
属性文件 |
camelCase、kebab-case 或者下划线表示法 |
标准列表语法,使用 |
YAML 文件 |
camelCase、kebab-case 或者下划线表示法 |
标准YAML列表语法或逗号分隔值 |
环境变量 |
使用大写字母格式并用下划线作为分隔符(参见 从环境变量绑定)。 |
带有下划线的数值(请参见从环境变量绑定) |
系统属性 |
camelCase、kebab-case 或者下划线表示法 |
标准列表语法,使用 |
我们建议,当可能时,属性应以小写字母连字符格式存储,例如my.person.first-name=Rod。 |
绑定映射
当绑定到 Map 属性时,您可能需要使用特殊的方括号表示法,以便保留原始的 key 值。
如果键未被 [] 包围,则任何非字母数字、- 或 . 的字符都将被移除。
例如,考虑将以下属性绑定到一个Map<String,String>中:
-
Properties
-
YAML
my.map[/key1]=value1
my.map[/key2]=value2
my.map./key3=value3
my:
map:
"[/key1]": "value1"
"[/key2]": "value2"
"/key3": "value3"
| 对于 YAML 文件,键需要被引号包围以便正确解析括号。 |
上述属性将绑定到一个 Map,其中 /key1、/key2 和 key3 作为映射中的键。
斜杠已从 key3 中移除,因为它未被方括号包围。
当绑定到标量值时,包含 . 的键不需要用 [] 包围。
标量值包括枚举以及 java.lang 包中的所有类型,但 Object 除外。
将 a.b=c 绑定到 Map<String, String> 将保留键中的 .,并返回一个包含条目 {"a.b"="c"} 的 Map。
对于任何其他类型,如果您的 key 包含 .,则需要使用方括号表示法。
例如,将 a.b=c 绑定到 Map<String, Object> 将返回一个包含条目 {"a"={"b"="c"}} 的 Map,而 [a.b]=c 将返回一个包含条目 {"a.b"="c"} 的 Map。
从环境变量绑定
大多数操作系统对环境变量的名称使用有严格的规则。
例如,在 Linux 中,shell 变量只能包含字母(从 a 到 z 或 A 到 Z),数字(从 0 到 9)或下划线字符 (_)。
按照惯例,Unix shell 变量的名称也会全部大写。
Spring Boot 的宽松绑定规则,在尽可能的情况下,旨在与这些命名限制保持兼容。
将规范形式的属性名称转换为环境变量名称,您可以遵循以下规则:
-
将点(
.)替换为下划线(_)。 -
移除任何破折号(
-)。 -
转换为大写。
例如,配置属性spring.main.log-startup-info会对应一个名为SPRING_MAIN_LOGSTARTUPINFO的环境变量。
环境变量也可用于绑定到对象列表。
要绑定到 List,变量名中的元素编号应使用下划线包围。
例如,配置属性my.service[0].other会使用一个名为MY_SERVICE_0_OTHER的环境变量。
支持从环境变量绑定的应用被应用于systemEnvironment属性源以及任何名称以-systemEnvironment结尾的额外属性源。
从环境变量绑定映射
当 Spring Boot 将环境变量绑定到属性类时,它会在绑定前将环境变量名称转换为小写。
大多数情况下,这一细节并不重要,除非是绑定到 Map 属性时。
Map 中的键始终为小写,如下例所示:
-
Java
-
Kotlin
import java.util.HashMap;
import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("my.props")
public class MyMapsProperties {
private final Map<String, String> values = new HashMap<>();
public Map<String, String> getValues() {
return this.values;
}
}
import org.springframework.boot.context.properties.ConfigurationProperties
@ConfigurationProperties("my.props")
class MyMapsProperties {
val values: Map<String, String> = HashMap()
}
当设置为 MY_PROPS_VALUES_KEY=value 时,values Map 包含一个 {"key"="value"} 条目。
只有环境变量 name 会被转换为小写,其值不会。
当设置 MY_PROPS_VALUES_KEY=VALUE 时,values Map 包含一个 {"key"="VALUE"} 条目。
缓存
宽松绑定使用缓存来提升性能。默认情况下,此缓存仅应用于不可变的属性源。
若要自定义此行为(例如为可变的属性源启用缓存),请使用 ConfigurationPropertyCaching。
合并复杂类型
当列表在一个以上的地方进行配置时,覆盖机制通过替换整个列表来实现。
例如,假设有一个名为MyPojo的对象,其中包含默认值为name的description和null属性。
以下示例从MyPojo中暴露了一组MyProperties对象:
-
Java
-
Kotlin
import java.util.ArrayList;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("my")
public class MyProperties {
private final List<MyPojo> list = new ArrayList<>();
public List<MyPojo> getList() {
return this.list;
}
}
import org.springframework.boot.context.properties.ConfigurationProperties
@ConfigurationProperties("my")
class MyProperties {
val list: List<MyPojo> = ArrayList()
}
考虑以下配置:
-
Properties
-
YAML
my.list[0].name=my name
my.list[0].description=my description
#---
spring.config.activate.on-profile=dev
my.list[0].name=my another name
my:
list:
- name: "my name"
description: "my description"
---
spring:
config:
activate:
on-profile: "dev"
my:
list:
- name: "my another name"
如果 dev 配置文件未激活,则 MyProperties.list 包含一个 MyPojo 条目,如前所述。
然而,如果启用了 dev 配置文件,list 仍然 只包含一个条目(名称为 my another name,描述为 null)。
此配置不会向列表中添加第二个 MyPojo 实例,也不会合并这些项。
当在多个配置文件中指定了 List 时,将使用优先级最高(且仅使用该)的一个。
请考虑以下示例:
-
Properties
-
YAML
my.list[0].name=my name
my.list[0].description=my description
my.list[1].name=another name
my.list[1].description=another description
#---
spring.config.activate.on-profile=dev
my.list[0].name=my another name
my:
list:
- name: "my name"
description: "my description"
- name: "another name"
description: "another description"
---
spring:
config:
activate:
on-profile: "dev"
my:
list:
- name: "my another name"
在前面的示例中,如果 dev 配置文件处于激活状态,则 MyProperties.list 包含一个MyPojo 条目(名称为 my another name,描述为 null)。
对于 YAML,逗号分隔的列表和 YAML 列表均可用于完全覆盖列表的内容。
对于 Map 属性,您可以绑定来自多个源的属性值。
但是,对于多个源中的同一属性,将使用优先级最高的那个。
以下示例展示了如何从 MyProperties 暴露一个 Map<String, MyPojo>:
-
Java
-
Kotlin
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("my")
public class MyProperties {
private final Map<String, MyPojo> map = new LinkedHashMap<>();
public Map<String, MyPojo> getMap() {
return this.map;
}
}
import org.springframework.boot.context.properties.ConfigurationProperties
@ConfigurationProperties("my")
class MyProperties {
val map: Map<String, MyPojo> = LinkedHashMap()
}
考虑以下配置:
-
Properties
-
YAML
my.map.key1.name=my name 1
my.map.key1.description=my description 1
#---
spring.config.activate.on-profile=dev
my.map.key1.name=dev name 1
my.map.key2.name=dev name 2
my.map.key2.description=dev description 2
my:
map:
key1:
name: "my name 1"
description: "my description 1"
---
spring:
config:
activate:
on-profile: "dev"
my:
map:
key1:
name: "dev name 1"
key2:
name: "dev name 2"
description: "dev description 2"
如果 dev 配置文件未激活,则 MyProperties.map 包含一个键为 key1 的条目(名称为 my name 1,描述为 my description 1)。
然而,如果启用了 dev 配置文件,则 map 包含两个条目,键分别为 key1(名称为 dev name 1,描述为 my description 1)和 key2(名称为 dev name 2,描述为 dev description 2)。
| preceding 融合并规则适用于所有属性源中的属性,而不仅仅是文件。 |
属性转换
Spring Boot 在绑定到 @ConfigurationProperties Bean 时,会尝试将外部应用程序属性强制转换为正确的类型。
如果您需要自定义类型转换,可以提供一个 ConversionService Bean(Bean 名称为 conversionService),或提供自定义属性编辑器(通过 CustomEditorConfigurer Bean),或提供自定义转换器(使用标注为 @ConfigurationPropertiesBinding 的 Bean 定义)。
|
用于属性转换的 Bean 会在应用程序生命周期的非常早期被请求,因此请确保限制您的 |
如果不需要用于配置键的强制转换,而仅依赖通过 @ConfigurationPropertiesBinding 限定的自定义转换器,您可能希望重命名您的自定义 ConversionService。
当使用 @ConfigurationPropertiesBinding 限定 @Bean 方法时,该方法应为 static,以避免出现 |
转换持续时间
Spring Boot 提供了专门的支持来表达持续时间。
如果您暴露一个 Duration 属性,则在 application properties 中可以使用以下格式:
-
一个常规的
long表示(默认使用毫秒作为单位,除非已指定@DurationUnit) -
时间值与单位结合的更易读格式(
10s表示10秒)
考虑以下示例:
-
Java
-
Kotlin
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.convert.DurationUnit;
@ConfigurationProperties("my")
public class MyProperties {
@DurationUnit(ChronoUnit.SECONDS)
private Duration sessionTimeout = Duration.ofSeconds(30);
private Duration readTimeout = Duration.ofMillis(1000);
// getters / setters...
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;
}
}
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.convert.DurationUnit
import java.time.Duration
import java.time.temporal.ChronoUnit
@ConfigurationProperties("my")
class MyProperties {
@DurationUnit(ChronoUnit.SECONDS)
var sessionTimeout = Duration.ofSeconds(30)
var readTimeout = Duration.ofMillis(1000)
}
指定会话超时为30秒,30、PT30S 和 30s 都是等价的。
读取超时为500ms可以使用以下任意一种形式指定:500、PT0.5S 和 500ms。
您可以使用任何受支持的单位。 这些是:
-
ns对应的是纳秒(nanoseconds) -
us用于微秒 -
ms对应毫秒 -
s对应秒 -
m对应分钟 -
h对应小时 -
d对应的是天数
默认单位是毫秒,可以使用 @DurationUnit 进行覆盖,如上例所示。
如果您更倾向于使用构造函数绑定,相同的属性也可以暴露,如下例所示:
-
Java
-
Kotlin
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.bind.DefaultValue;
import org.springframework.boot.convert.DurationUnit;
@ConfigurationProperties("my")
public class MyProperties {
// fields...
private final Duration sessionTimeout;
private final Duration readTimeout;
public MyProperties(@DurationUnit(ChronoUnit.SECONDS) @DefaultValue("30s") Duration sessionTimeout,
@DefaultValue("1000ms") Duration readTimeout) {
this.sessionTimeout = sessionTimeout;
this.readTimeout = readTimeout;
}
// getters...
public Duration getSessionTimeout() {
return this.sessionTimeout;
}
public Duration getReadTimeout() {
return this.readTimeout;
}
}
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.context.properties.bind.DefaultValue
import org.springframework.boot.convert.DurationUnit
import java.time.Duration
import java.time.temporal.ChronoUnit
@ConfigurationProperties("my")
class MyProperties(@param:DurationUnit(ChronoUnit.SECONDS) @param:DefaultValue("30s") val sessionTimeout: Duration,
@param:DefaultValue("1000ms") val readTimeout: Duration)
如果您正在升级 Long 属性,请确保在单位不是毫秒时定义单位(使用 @DurationUnit)。
这样做可以在支持更丰富格式的同时提供透明的升级路径。 |
转换周期
除了持续时间外,Spring Boot 还可以使用 Period 类型。
以下格式可用于应用程序属性:
-
一个常规的
int表示(默认使用天作为单位,除非已指定@PeriodUnit) -
值和单位成对出现的更简单的格式(
1y3d表示1年和3天)
以下单元支持简单的格式:
-
y对年 -
m对应月份 -
w对应周数 -
d对应的是天数
Period 类型实际上从不存储周数,它是一个表示 |
转换数据大小
-
常规的
long表示法(默认使用字节作为单位,除非已指定@DataSizeUnit) -
读起来更清晰的格式,其中值和单位是结合在一起的(
10MB表示10兆字节)
考虑以下示例:
-
Java
-
Kotlin
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.convert.DataSizeUnit;
import org.springframework.util.unit.DataSize;
import org.springframework.util.unit.DataUnit;
@ConfigurationProperties("my")
public class MyProperties {
@DataSizeUnit(DataUnit.MEGABYTES)
private DataSize bufferSize = DataSize.ofMegabytes(2);
private DataSize sizeThreshold = DataSize.ofBytes(512);
// getters/setters...
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;
}
}
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.convert.DataSizeUnit
import org.springframework.util.unit.DataSize
import org.springframework.util.unit.DataUnit
@ConfigurationProperties("my")
class MyProperties {
@DataSizeUnit(DataUnit.MEGABYTES)
var bufferSize = DataSize.ofMegabytes(2)
var sizeThreshold = DataSize.ofBytes(512)
}
要指定10兆字节的缓冲区大小,10和10MB是等价的。可以将256字节的大小阈值指定为256或256B。
您可以使用任何受支持的单位。 这些是:
-
B用于表示字节 -
KB对应千字节 -
MB对应的是兆字节 -
GB对应的是吉字节 -
TB对应太字节
默认单位是字节,可以使用 @DataSizeUnit 进行覆盖,如上例所示。
如果您更倾向于使用构造函数绑定,相同的属性也可以暴露,如下例所示:
-
Java
-
Kotlin
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.bind.DefaultValue;
import org.springframework.boot.convert.DataSizeUnit;
import org.springframework.util.unit.DataSize;
import org.springframework.util.unit.DataUnit;
@ConfigurationProperties("my")
public class MyProperties {
// fields...
private final DataSize bufferSize;
private final DataSize sizeThreshold;
public MyProperties(@DataSizeUnit(DataUnit.MEGABYTES) @DefaultValue("2MB") DataSize bufferSize,
@DefaultValue("512B") DataSize sizeThreshold) {
this.bufferSize = bufferSize;
this.sizeThreshold = sizeThreshold;
}
// getters...
public DataSize getBufferSize() {
return this.bufferSize;
}
public DataSize getSizeThreshold() {
return this.sizeThreshold;
}
}
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.context.properties.bind.DefaultValue
import org.springframework.boot.convert.DataSizeUnit
import org.springframework.util.unit.DataSize
import org.springframework.util.unit.DataUnit
@ConfigurationProperties("my")
class MyProperties(@param:DataSizeUnit(DataUnit.MEGABYTES) @param:DefaultValue("2MB") val bufferSize: DataSize,
@param:DefaultValue("512B") val sizeThreshold: DataSize)
如果您正在升级 Long 属性,请确保在该属性单位不是字节时定义其单位(使用 @DataSizeUnit)。
这样做可以在支持更丰富格式的同时,提供透明的升级路径。 |
@ConfigurationProperties 验证
Spring Boot 尝试验证任何使用 Spring 的 @Validated 注解进行标注的 @ConfigurationProperties 类。
您可以直接在配置类上使用 JSR-303 jakarta.validation 约束注解。
为此,请确保类路径上存在兼容的 JSR-303 实现,然后将约束注解添加到您的字段中,如下例所示:
-
Java
-
Kotlin
import java.net.InetAddress;
import jakarta.validation.constraints.NotNull;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
@ConfigurationProperties("my.service")
@Validated
public class MyProperties {
@NotNull
private InetAddress remoteAddress;
// getters/setters...
public InetAddress getRemoteAddress() {
return this.remoteAddress;
}
public void setRemoteAddress(InetAddress remoteAddress) {
this.remoteAddress = remoteAddress;
}
}
import jakarta.validation.constraints.NotNull
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.validation.annotation.Validated
import java.net.InetAddress
@ConfigurationProperties("my.service")
@Validated
class MyProperties {
var remoteAddress: @NotNull InetAddress? = null
}
您还可以通过使用 @Validated 注解创建配置属性的 @Bean 方法来触发验证。 |
要将验证级联到嵌套属性,关联字段必须使用 @Valid 进行注解。
以下示例基于前面的 MyProperties 示例构建:
-
Java
-
Kotlin
import java.net.InetAddress;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
@ConfigurationProperties("my.service")
@Validated
public class MyProperties {
@NotNull
private InetAddress remoteAddress;
@Valid
private final Security security = new Security();
// getters/setters...
public InetAddress getRemoteAddress() {
return this.remoteAddress;
}
public void setRemoteAddress(InetAddress remoteAddress) {
this.remoteAddress = remoteAddress;
}
public Security getSecurity() {
return this.security;
}
public static class Security {
@NotEmpty
private String username;
// getters/setters...
public String getUsername() {
return this.username;
}
public void setUsername(String username) {
this.username = username;
}
}
}
import jakarta.validation.Valid
import jakarta.validation.constraints.NotEmpty
import jakarta.validation.constraints.NotNull
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.validation.annotation.Validated
import java.net.InetAddress
@ConfigurationProperties("my.service")
@Validated
class MyProperties {
var remoteAddress: @NotNull InetAddress? = null
@Valid
val security = Security()
class Security {
@NotEmpty
var username: String? = null
}
}
您还可以通过创建一个名为 configurationPropertiesValidator 的 Bean 定义来添加自定义的 Spring Validator。
@Bean 方法应声明为 static。
配置属性验证器在应用程序生命周期的非常早期阶段创建,将 @Bean 方法声明为 static 可以在无需实例化 @Configuration 类的情况下创建该 Bean。
这样做可以避免因过早实例化而可能引发的任何问题。
spring-boot-actuator 模块包含一个端点,用于暴露所有 @ConfigurationProperties Bean。
请将您的网页浏览器指向 /actuator/configprops,或使用等效的 JMX 端点。
有关详细信息,请参阅 生产就绪功能 部分。 |
@ConfigurationProperties 与 @Value
@Value 注解是核心容器特性,它不提供与类型安全配置属性相同的功能。
下表总结了 @ConfigurationProperties 和 @Value 支持的功能:
| 特性 | @ConfigurationProperties |
@Value |
|---|---|---|
是的 |
受限(参见下方注释) |
|
是的 |
No |
|
|
No |
是的 |
|
如果您确实想使用 例如, |
如果您为自己的组件定义了一组配置键,我们建议您将它们分组到一个使用 @ConfigurationProperties 注解的 POJO 中。
这样做将为您提供一个结构化的、类型安全的对象,您可以将其注入到您自己的 Bean 中。