外部化配置
Spring Boot 允许你将配置外部化,以便在不同环境中使用相同的应用程序代码。你可以使用多种外部配置源,包括 Java properties 文件、YAML 文件、环境变量和命令行参数。
属性值可以通过使用 @Value
注解直接注入到你的 Bean 中,通过 Spring 的 Environment
抽象访问,或者通过 @ConfigurationProperties
绑定到结构化对象。
Spring Boot 使用了一种非常特定的 PropertySource
顺序,旨在允许合理地覆盖值。后加载的属性源可以覆盖先加载的属性源中定义的值。属性源按以下顺序考虑:
-
默认属性(通过设置
SpringApplication.setDefaultProperties(Map)
指定)。 -
你的
@Configuration
类上的@PropertySource
注解。请注意,此类属性源直到应用程序上下文刷新时才会添加到Environment
中。这对于配置某些属性(例如logging.*
和spring.main.*
,它们在刷新开始之前就被读取)来说太晚了。 -
配置数据(例如
application.properties
文件)。 -
一个
RandomValuePropertySource
,其属性仅包含在random.*
中。 -
操作系统环境变量。
-
Java 系统属性(
System.getProperties()
)。 -
来自
java:comp/env
的 JNDI 属性。 -
ServletContext
初始化参数。 -
ServletConfig
初始化参数。 -
来自
SPRING_APPLICATION_JSON
的属性(嵌入在环境变量或系统属性中的内联 JSON)。 -
命令行参数。
-
测试中的
properties
属性。在@SpringBootTest
和用于测试应用程序特定部分的测试注解上可用。 -
你的测试中的
@DynamicPropertySource
注解。 -
你的测试中的
@TestPropertySource
注解。 -
当 devtools 处于活动状态时,位于
$HOME/.config/spring-boot
目录中的Devtools 全局设置属性。
配置数据文件按以下顺序考虑:
-
打包在你的 jar 内部的应用程序属性(
application.properties
及其 YAML 变体)。 -
打包在你的 jar 内部的特定 Profile 的应用程序属性(
application-{profile}.properties
及其 YAML 变体)。 -
打包在你的 jar 外部的应用程序属性(
application.properties
及其 YAML 变体)。 -
打包在你的 jar 外部的特定 Profile 的应用程序属性(
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 shell 中,可以在命令行上以环境变量的形式提供 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
如果位置是可选的,并且你不介意它们不存在,请使用前缀 optional: 。 |
spring.config.name 、spring.config.location 和 spring.config.additional-location 在很早的时候就会被使用,以确定需要加载哪些文件。它们必须定义为环境变量属性(通常是操作系统环境变量、系统属性或命令行参数)。 |
如果 spring.config.location
包含目录(而不是文件),则它们应以 /
结尾。运行时,在加载之前,会为其附加从 spring.config.name
生成的名称。在 spring.config.location
中指定的文件会直接导入。
目录和文件位置值都会被扩展,以检查特定 profile 的文件。例如,如果你的 spring.config.location 是 classpath:myconfig.properties ,你也会发现相应的 classpath:myconfig-<profile>.properties 文件被加载。 |
在大多数情况下,你添加的每个 spring.config.location
项都会引用单个文件或目录。位置按照定义的顺序处理,后面的位置可以覆盖前面位置的值。
如果你有一个复杂的位置设置,并且使用了特定 profile 的配置文件,你可能需要提供进一步的提示,以便 Spring Boot 知道它们应该如何分组。位置组是同一级别考虑的位置集合。例如,你可能希望先将所有类路径位置分组,然后将所有外部位置分组。位置组内的项应使用 ;
分隔。详情请参阅特定 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
选择的任何其他基本名称)中为应用程序提供默认值。然后,这些默认值可以在运行时通过位于自定义位置之一的不同文件进行覆盖。
可选位置
默认情况下,当指定的配置数据位置不存在时,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: 位置中使用通配符。 |
特定 Profile 文件
除了 application
属性文件外,Spring Boot 还会尝试使用命名约定 application-{profile}
加载特定 profile 的文件。例如,如果你的应用程序激活了名为 prod
的 profile 并使用 YAML 文件,则会同时考虑 application.yaml
和 application-prod.yaml
。
特定 profile 的属性会从与标准 application.properties
相同的位置加载,特定 profile 的文件总是会覆盖非特定文件。如果指定了多个 profile,则应用后一个优先的策略。例如,如果 spring.profiles.active
属性指定了 prod,live
这两个 profile,则 application-prod.properties
中的值可以被 application-live.properties
中的值覆盖。
后一个优先的策略适用于位置组级别。 例如,沿用上面 /cfg application-live.properties /ext application-live.properties application-prod.properties 当我们设置
当我们改为使用
|
Environment
有一组默认 profile(默认情况下是 [default]
),如果在没有设置活动 profile 的情况下使用它们。换句话说,如果没有显式激活任何 profile,则会考虑来自 application-default
的属性。
属性文件只会加载一次。如果你已经直接导入了特定 profile 的属性文件,则不会再次导入。 |
导入额外数据
应用程序属性可以使用 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
重新定义为不同的值。
一个导入只会导入一次,无论它被声明多少次。导入在 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
键下指定多个位置。位置将按照定义的顺序处理,后导入的将优先。
在适当的时候,也会考虑特定 Profile 的变体进行导入。上面的例子会导入 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)上运行应用程序时,您经常需要读取平台提供的配置值。通常使用环境变量来实现此目的,但这可能存在缺点,特别是当值需要保密时。
作为环境变量的替代方案,现在许多云平台允许您将配置映射到挂载的数据卷中。例如,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
属性。
配置树下的文件夹和文件名称构成属性名称。在上面的例子中,要以 username 和 password 访问属性,您可以将 spring.config.import 设置为 optional:configtree:/etc/config/myapp 。 |
带点符号的文件名也能正确映射。例如,在上面的例子中,/etc/config 目录下的一个名为 myapp.username 的文件将在 Environment 中产生一个 myapp.username 属性。 |
配置树值可以根据期望的内容绑定到字符串 String 和 byte[] 类型。 |
如果您有多个配置树要从同一个父文件夹导入,您可以使用通配符快捷方式。任何以 /*/
结尾的 configtree:
位置都将把所有直接子文件夹作为配置树导入。与非通配符导入一样,每个配置树下的文件夹和文件名称构成属性名称。
例如,给定以下卷
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 secrets。当 Docker swarm 服务被授予访问 secret 的权限时,该 secret 将被挂载到容器中。例如,如果一个名为 db.password
的 secret 挂载在 /run/secrets/
位置,您可以使用以下方式将 db.password
提供给 Spring Environment
-
Properties
-
YAML
spring.config.import=optional:configtree:/run/secrets/
spring:
config:
import: "optional:configtree:/run/secrets/"
属性占位符
application.properties
和 application.yaml
中的值在使用时会通过现有的 Environment
进行过滤,因此您可以引用先前定义的值(例如,来自系统属性或环境变量)。标准 ${name}
属性占位符语法可以在值的任何地方使用。属性占位符还可以使用 :
来分隔默认值和属性名称,从而指定默认值,例如 ${name:default}
。
下面示例展示了使用和不使用默认值的占位符
-
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
。
在占位符中引用属性名称时,应始终使用它们的规范形式(仅使用小写字母的 kebab-case)。这将允许 Spring Boot 使用与宽松绑定 例如, |
您还可以使用此技术创建现有 Spring Boot 属性的“短”变体。有关详细信息,请参阅“How-to Guides”中的使用“短”命令行参数一节。 |
使用多文档文件
Spring Boot 允许您将单个物理文件拆分为多个逻辑文档,每个文档独立添加。文档按顺序从上到下处理。后面的文档可以覆盖前面定义的属性。
对于 application.yaml
文件,使用标准的 YAML 多文档语法。三个连续的连字符表示一个文档的结束和下一个文档的开始。
例如,以下文件包含两个逻辑文档
spring:
application:
name: "MyApp"
---
spring:
application:
name: "MyCloudApp"
config:
activate:
on-cloud-platform: "kubernetes"
对于 application.properties
文件,使用特殊的 #---
或 !---
注释来标记文档分割
spring.application.name=MyApp
#---
spring.application.name=MyCloudApp
spring.config.activate.on-cloud-platform=kubernetes
属性文件分隔符不能有前导空白,并且必须精确包含三个连字符。分隔符正上方和正下方的行不能使用相同的注释前缀。 |
多文档属性文件通常与激活属性(如 spring.config.activate.on-profile )结合使用。有关详细信息,请参阅下一节。 |
不能使用 @PropertySource 或 @TestPropertySource 注解加载多文档属性文件。 |
激活属性
有时,在满足特定条件时才激活给定属性集会很有用。例如,您可能有一些属性仅在特定 profile 处于活动状态时才相关。
您可以使用 spring.config.activate.*
有条件地激活属性文档。
可用的激活属性如下
属性 | 注意 |
---|---|
|
文档要处于活动状态必须匹配的 profile 表达式,或者必须匹配至少一个文档才能处于活动状态的 profile 表达式列表。 |
|
必须检测到的 |
例如,以下配置指定第二个文档仅在 Kubernetes 上运行时处于活动状态,并且仅当“prod”或“staging” profile 处于活动状态时
-
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 的超集,因此是一种方便的格式,用于指定分层配置数据。只要您的 classpath 中包含 SnakeYAML 库,SpringApplication
类会自动支持 YAML 作为属性文件的替代方案。
如果您使用 starter,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
同样,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
对于注入随机值(例如,用于 secret 或测试用例)非常有用。它可以生成整数、长整数、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]}"
random.int*
语法是 OPEN value (,max) CLOSE
,其中 OPEN,CLOSE
是任何字符,value,max
是整数。如果提供了 max
,则 value
是最小值,而 max
是最大值(不包含)。
配置系统环境变量
Spring Boot 支持为环境变量设置前缀。如果系统环境被具有不同配置要求的多个 Spring Boot 应用程序共享,这将非常有用。系统环境变量的前缀可以直接在 SpringApplication
上设置。
例如,如果将前缀设置为 input
,则像 remote.timeout
这样的属性也将在系统环境中解析为 input.remote.timeout
。
类型安全的配置属性
使用 @Value("${property}")
注解注入配置属性有时可能很麻烦,特别是当您处理多个属性或数据具有分层性质时。Spring Boot 提供了一种处理属性的替代方法,允许强类型 bean 控制和验证应用程序的配置。
JavaBean 属性绑定
可以绑定声明标准 JavaBean 属性的 bean,如以下示例所示
-
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"))
}
}
上面的 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 类的属性(通过 properties 文件、YAML 文件、环境变量和其他机制配置)是公共 API,但类本身的访问器(getter/setter)并非 intended 直接使用。 |
这种安排依赖于默认的空构造函数,并且 getter 和 setter 通常是必需的,因为绑定是通过标准的 Java Beans 属性描述符进行的,就像在 Spring MVC 中一样。在以下情况下可以省略 setter
有些人使用 Project Lombok 自动添加 getter 和 setter。请确保 Lombok 不为此类类型生成任何特定构造函数,因为它由容器自动用于实例化对象。 最后,仅考虑标准的 Java Bean 属性,不支持静态属性的绑定。 |
构造函数绑定
上一节的示例可以用不可变的方式重写,如以下示例所示
-
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>)
}
在这种设置中,存在单个带参数的构造函数意味着应使用构造函数绑定。这意味着 binder 将找到一个带有您希望绑定的参数的构造函数。如果您的类有多个构造函数,可以使用 @ConstructorBinding
注解指定用于构造函数绑定的构造函数。对于具有单个带参数构造函数的类,要退出构造函数绑定,该构造函数必须用 @Autowired
注解或设为 private
。构造函数绑定可与 records 一起使用。除非您的 record 有多个构造函数,否则无需使用 @ConstructorBinding
。
构造函数绑定类的嵌套成员(如上面示例中的 Security
)也将通过其构造函数进行绑定。
可以使用构造函数参数和 record 组件上的 @DefaultValue
指定默认值。转换服务将应用于将注解的 String
值强制转换为缺失属性的目标类型。
回到前面的例子,如果没有属性绑定到 Security
,则 MyProperties
实例将包含 security
的 null
值。即使没有属性绑定到它(使用 Kotlin 时,这需要将 Security
的 username
和 password
参数声明为可空,因为它们没有默认值),若要使其包含非 null 的 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 一起使用,因为它主要 intended 用作返回类型。因此,它不太适合配置属性注入。为了与其他类型属性保持一致,如果您声明一个 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。对于特殊情况,可以使用 setter 注入或框架提供的任何 *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,请将 @ConfigurationProperties
添加到其 bean 注册中,如以下示例所示
-
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(prefix = "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(prefix = "another")
fun anotherComponent(): AnotherComponent = AnotherComponent()
}
任何使用 another
前缀定义的 JavaBean 属性都将以类似于前面 SomeProperties
示例的方式映射到该 AnotherComponent
bean。
宽松绑定
Spring Boot 使用一些宽松规则将 Environment
属性绑定到 @ConfigurationProperties
bean,因此 Environment
属性名和 bean 属性名之间无需完全匹配。这有用的常见示例包括使用连字符分隔的环境属性(例如,context-path
绑定到 contextPath
)和首字母大写的环境属性(例如,PORT
绑定到 port
)。
例如,考虑以下 @ConfigurationProperties
类
-
Java
-
Kotlin
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "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(prefix = "my.main-project.person")
class MyPersonProperties {
var firstName: String? = null
}
通过上述代码,以下所有属性名称都可以使用
属性 | 注意 |
---|---|
|
Kebab 命名法,推荐用于 |
|
标准的驼峰命名法语法。 |
|
下划线表示法,是 |
|
大写格式,推荐在系统环境变量中使用。 |
注解的 prefix 值必须是 kebab 命名法(小写并用 - 分隔,例如 my.main-project.person )。 |
属性源 | 简单类型 | 列表 |
---|---|---|
Properties 文件 |
驼峰命名法、kebab 命名法或下划线表示法 |
使用 |
YAML 文件 |
驼峰命名法、kebab 命名法或下划线表示法 |
标准的 YAML 列表语法或逗号分隔值 |
环境变量 |
使用下划线作为分隔符的大写格式(参见 从环境变量绑定)。 |
被下划线包围的数字值(参见 从环境变量绑定) |
系统属性 |
驼峰命名法、kebab 命名法或下划线表示法 |
使用 |
我们建议,如果可能,属性以小写 kebab 格式存储,例如 my.person.first-name=Rod 。 |
绑定 Map
当绑定到 Map
属性时,您可能需要使用特殊的括号表示法,以便保留原始的 key
值。如果 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 文件,括号需要用引号括起来,以便正确解析 key。 |
上述属性将绑定到一个 Map
,其 key 为 /key1
、/key2
和 key3
。key3
中的斜杠已被移除,因为它没有被方括号包围。
当绑定到标量值时,包含 .
的 key 不需要被 []
包围。标量值包括枚举以及 java.lang
包中的所有类型(除了 Object
)。将 a.b=c
绑定到 Map<String, String>
将保留 key 中的 .
,并返回一个包含条目 {"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 的宽松绑定规则尽可能地设计为与这些命名限制兼容。
要将规范形式(canonical-form)的属性名称转换为环境变量名称,您可以遵循以下规则
-
将点号 (
.
) 替换为下划线 (_
)。 -
移除所有连字符 (
-
)。 -
转换为大写。
例如,配置属性 spring.main.log-startup-info
将对应名为 SPRING_MAIN_LOGSTARTUPINFO
的环境变量。
环境变量也可用于绑定对象列表。要绑定到 List
,变量名称中的元素索引应被下划线包围。
例如,配置属性 my.service[0].other
将使用名为 MY_SERVICE_0_OTHER
的环境变量。
从环境变量绑定的支持应用于 systemEnvironment
属性源以及任何名称以 -systemEnvironment
结尾的附加属性源。
从环境变量绑定 Map
当 Spring Boot 将环境变量绑定到属性类时,它会在绑定前将环境变量名称转换为小写。大多数情况下,这个细节并不重要,除非绑定到 Map
属性。
Map
中的 key 始终是小写的,如下例所示
-
Java
-
Kotlin
import java.util.HashMap;
import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "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(prefix = "my.props")
class MyMapsProperties {
val values: Map<String, String> = HashMap()
}
当设置 MY_PROPS_VALUES_KEY=value
时,values
Map
包含一个条目 {"key"="value"}
。
只有环境变量的 名称 被转换为小写,而不是值。当设置 MY_PROPS_VALUES_KEY=VALUE
时,values
Map
包含一个条目 {"key"="VALUE"}
。
缓存
宽松绑定使用缓存来提高性能。默认情况下,此缓存仅应用于不可变属性源。要自定义此行为,例如为可变属性源启用缓存,请使用 ConfigurationPropertyCaching
。
合并复杂类型
当列表在多个地方配置时,覆盖的工作方式是替换整个列表。
例如,假设一个 MyPojo
对象,其 name
和 description
属性默认为 null
。以下示例从 MyProperties
中暴露了一个 MyPojo
对象列表
-
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
profile 未激活,MyProperties.list
包含一个 MyPojo
条目,如前所述。但是,如果 dev
profile 启用,list
仍然只包含一个条目(名称为 my another name
,描述为 null
)。此配置不会向列表中添加第二个 MyPojo
实例,并且不会合并项目。
当 List
在多个 profile 中指定时,只使用优先级最高的那一个。考虑以下示例
-
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
profile 处于激活状态,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
profile 未激活,MyProperties.map
包含一个 key 为 key1
的条目(名称为 my name 1
,描述为 my description 1
)。但是,如果 dev
profile 启用,map
包含两个条目,key 分别为 key1
(名称为 dev name 1
,描述为 my description 1
)和 key2
(名称为 dev name 2
,描述为 dev description 2
)。
上述合并规则适用于所有属性源的属性,而不仅仅是文件。 |
属性转换
当 Spring Boot 绑定到 @ConfigurationProperties
bean 时,它会尝试将外部应用属性强制转换为正确的类型。如果您需要自定义类型转换,可以提供一个 ConversionService
bean( bean 名称为 conversionService
),或通过 CustomEditorConfigurer
bean 提供自定义属性编辑器,或提供标记为 @ConfigurationPropertiesBinding
注解的 bean 定义的自定义转换器。
由于此 bean 在应用生命周期的早期就被请求,请确保限制您的 ConversionService 所使用的依赖项。通常,您需要的任何依赖项在创建时可能尚未完全初始化。如果您的自定义 ConversionService 不是配置 key 强制转换所必需的,并且只依赖于使用 @ConfigurationPropertiesBinding 限定的自定义转换器,则您可能需要重命名它。 |
转换 Duration
Spring Boot 为表示持续时间(duration)提供了专门的支持。如果您暴露 Duration
属性,应用属性中可使用以下格式
-
常规的
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
表示纳秒 -
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 )。这样做可以提供一个透明的升级路径,同时支持更丰富的格式。 |
转换 Period
除了持续时间(duration),Spring Boot 还可以使用 Period
类型。应用属性中可使用以下格式
-
常规的
int
表示法(除非指定了@PeriodUnit
,否则默认单位为天) -
更简单的格式,其中值和单位对结合(
1y3d
表示 1 年零 3 天)
简单格式支持以下单位
-
y
表示年 -
m
表示月 -
w
表示周 -
d
表示天
Period 类型实际上从不存储周数,它只是一个表示“7 天”的快捷方式。 |
转换数据大小
-
常规的
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 验证
每当 @ConfigurationProperties
类使用 Spring 的 @Validated
注解时,Spring Boot 会尝试对其进行验证。您可以直接在配置类上使用 JSR-303 jakarta.validation
约束注解。为此,请确保 classpath 中存在兼容的 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 允许创建 bean,而无需实例化 @Configuration
类。这样做可以避免早期实例化可能导致的任何问题。
spring-boot-actuator 模块包含一个暴露所有 @ConfigurationProperties bean 的端点。在您的网络浏览器中访问 /actuator/configprops 或使用等效的 JMX 端点。详情请参见 生产就绪特性 部分。 |
@ConfigurationProperties 对比 @Value
@Value
注解是核心容器特性,它不提供与类型安全的配置属性相同的功能。下表总结了 @ConfigurationProperties
和 @Value
支持的功能
特性 | @ConfigurationProperties |
@Value |
---|---|---|
是 |
有限(参见 下面的说明) |
|
是 |
否 |
|
|
否 |
是 |
如果您确实想使用 例如, |
如果您为自己的组件定义了一组配置 key,我们建议您将它们组合到一个使用 @ConfigurationProperties
注解的 POJO 中。这样做将为您提供结构化、类型安全的对象,您可以将其注入到自己的 bean 中。