外部化配置
Spring Boot 允许你外部化你的配置,以便你可以在不同的环境中使用相同的应用程序代码。你可以使用各种外部配置源,包括 Java 属性文件、YAML 文件、环境变量和命令行参数。
可以使用@Value
注解将属性值直接注入到你的 Bean 中,通过 Spring 的Environment
抽象访问属性值,或者通过@ConfigurationProperties
绑定到结构化对象。
Spring Boot 使用非常特殊的PropertySource
顺序,旨在允许对值进行合理的覆盖。后面的属性源可以覆盖前面属性源中定义的值。源按以下顺序考虑:
-
默认属性(通过设置
SpringApplication.setDefaultProperties
指定)。 -
在您的
@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包内(
application-{profile}.properties
和YAML变体)。 -
应用程序属性位于您的jar包之外(
application.properties
和YAML变体)。 -
特定于配置文件的应用程序属性位于您的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 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/
子目录的直接子目录
-
列表按优先级排序(较低项目的数值会覆盖较早的数值)。加载的文件中的文档作为PropertySources
添加到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
中指定的文件将直接导入。
目录和文件位置值也会扩展为检查特定于配置文件的文件。例如,如果您有spring.config.location 为classpath:myconfig.properties ,您还将找到加载相应的classpath:myconfig-<profile>.properties 文件。 |
在大多数情况下,您添加的每个spring.config.location
项目都将引用单个文件或目录。位置按定义的顺序处理,后面的位置可以覆盖前面的位置的值。
如果您有一个复杂的定位设置,并且您使用特定于配置文件的配置文件,您可能需要提供进一步的提示,以便Spring Boot知道如何对它们进行分组。位置组是所有在同一级别考虑的位置的集合。例如,您可能希望对所有类路径位置进行分组,然后对所有外部位置进行分组。位置组中的项目应以;
分隔。有关更多详细信息,请参见特定于配置文件的文件部分中的示例。
使用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
文件丢失。
如果您想忽略所有ConfigDataLocationNotFoundExceptions
并始终继续启动您的应用程序,可以使用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
相同的位置加载,特定于配置文件的文件始终会覆盖非特定文件。如果指定了多个配置文件,则应用“最后获胜”策略。例如,如果通过spring.profiles.active
属性指定了配置文件prod,live
,则application-prod.properties
中的值可以被application-live.properties
中的值覆盖。
“最后获胜”策略适用于位置组级别。 例如,继续我们上面提到的 /cfg application-live.properties /ext application-live.properties application-prod.properties 当我们的
当我们使用
|
Environment
有一组默认配置文件(默认为[default]
),如果未设置活动配置文件,则使用这些默认配置文件。换句话说,如果没有明确激活任何配置文件,则会考虑来自application-default
的属性。
属性文件仅加载一次。如果您已经直接导入了特定于配置文件的属性文件,则不会再次导入。 |
导入附加数据
应用程序属性可以使用spring.config.import
属性从其他位置导入更多配置数据。导入过程在发现导入时进行处理,并将被视为插入声明导入的文件正下方的附加文档。
例如,您的类路径application.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
重新定义为不同的值。
无论声明多少次,导入都只导入一次。在属性/yaml文件中单个文档内定义导入的顺序无关紧要。例如,以下两个示例产生相同的结果:
-
属性
-
YAML
spring.config.import=my.properties
my.property=value
spring:
config:
import: "my.properties"
my:
property: "value"
-
属性
-
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 属性、YAML 和配置树。 第三方 jar 可以提供对其他技术的支持(不需要文件是本地的)。例如,您可以想象配置数据来自外部存储,例如 Consul、Apache ZooKeeper 或 Netflix Archaius。 如果您想支持自己的位置,请参阅 |
导入无扩展名文件
一些云平台无法向卷装载的文件添加文件扩展名。要导入这些无扩展名的文件,您需要给 Spring Boot 一个提示,以便它知道如何加载它们。您可以通过在方括号中添加扩展名提示来实现。
例如,假设您有一个/etc/config/myconfig
文件,您希望将其作为 yaml 导入。您可以使用以下方法从您的application.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 或属性文件。对于第二种情况,您需要使用configtree:
前缀,以便 Spring Boot 知道它需要将所有文件作为属性公开。
例如,假设 Kubernetes 已挂载以下卷:
etc/
config/
myapp/
username
password
username
文件的内容将是一个配置值,password
文件的内容将是一个秘密。
要导入这些属性,您可以将以下内容添加到您的application.properties
或application.yaml
文件中:
-
属性
-
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/*/
作为导入位置。
-
属性
-
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 环境:
-
属性
-
YAML
spring.config.import=optional:configtree:/run/secrets/
spring:
config:
import: "optional:configtree:/run/secrets/"
属性占位符
使用application.properties
和application.yaml
中的值时,它们会通过现有的Environment
进行过滤,因此您可以参考以前定义的值(例如,来自系统属性或环境变量)。可以在任何值中使用标准${name}
属性占位符语法。属性占位符还可以使用:
指定默认值,以分隔默认值和属性名称,例如${name:default}
。
以下示例显示了使用带默认值和不带默认值的占位符:
-
属性
-
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 属性的“简短”变体。有关详细信息,请参阅“操作指南”中的使用“简短”命令行参数部分。 |
使用多文档文件
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 注解加载多文档属性文件。 |
激活属性
有时仅在满足特定条件时才激活给定的属性集非常有用。例如,您可能有一些属性仅在激活特定配置文件时才相关。
您可以使用spring.config.activate.*
有条件地激活属性文档。
以下激活属性可用
属性 | 说明 |
---|---|
|
必须匹配的配置文件表达式,才能激活文档。 |
|
必须检测到的 |
例如,以下内容指定仅在 Kubernetes 上运行且仅在激活“prod”或“staging”配置文件时才激活第二个文档。
-
属性
-
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
。有关详细信息,请参见启动前自定义环境或应用程序上下文。
如果您需要一种安全的方式来存储凭据和密码,Spring Cloud Vault 项目提供对在 HashiCorp Vault 中存储外部化配置的支持。
使用 YAML
如果您使用启动器,则spring-boot-starter 会自动提供 SnakeYAML。 |
将 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
使用[index] 表示法的属性可以使用 Spring Boot 的Binder 类绑定到 Java List 或Set 对象。有关详细信息,请参见下面的类型安全的配置属性部分。 |
不能使用@PropertySource 或@TestPropertySource 注解加载 YAML 文件。因此,如果您需要以这种方式加载值,则需要使用属性文件。 |
配置随机值
RandomValuePropertySource
用于注入随机值(例如,注入到密钥或测试用例中)。它可以生成整数、长整数、UUID 或字符串,如下例所示:
-
属性
-
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
。
映射到 Spring Boot 中可用的@ConfigurationProperties 类的属性(通过属性文件、YAML 文件、环境变量和其他机制进行配置)是公共 API,但类本身的访问器(getter/setter)不应直接使用。 |
这种安排依赖于默认的空构造函数,并且 getter 和 setter 通常是必需的,因为绑定是通过标准 Java Bean 属性描述符进行的,就像在 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
。构造函数绑定可与记录一起使用。除非您的记录具有多个构造函数,否则无需使用@ConstructorBinding
。
构造函数绑定类的嵌套成员(例如,上面的示例中的Security
)也将通过其构造函数进行绑定。
可以使用构造函数参数和记录组件上的@DefaultValue
指定默认值。转换服务将应用于将注解的String
值强制转换为缺少属性的目标类型。
参考前面的示例,如果没有属性绑定到Security
,则MyProperties
实例将包含security
的null
值。为了使其即使在没有属性绑定到它时也包含Security
的非空实例(在使用 Kotlin 时,这将需要将Security
的username
和password
参数声明为可空,因为它们没有默认值),请使用空的@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 ,这将自动发生。 |
不建议将java.util.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。对于极端情况,可以使用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,请在其 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(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
}
使用上述代码,可以使用以下属性名称:
属性 | 说明 |
---|---|
|
短横线命名法,建议在 |
|
标准驼峰命名法。 |
|
下划线命名法,这是 |
|
大写格式,建议在使用系统环境变量时使用。 |
注解的prefix 值必须使用短横线命名法(小写并用- 分隔,例如my.main-project.person )。 |
属性源 | 简单 | 列表 |
---|---|---|
属性文件 |
驼峰命名法、短横线命名法或下划线命名法 |
使用 |
YAML 文件 |
驼峰命名法、短横线命名法或下划线命名法 |
标准 YAML 列表语法或逗号分隔值 |
环境变量 |
使用下划线作为分隔符的大写格式(请参阅从环境变量绑定)。 |
用下划线包围的数字值(请参阅从环境变量绑定) |
系统属性 |
驼峰命名法、短横线命名法或下划线命名法 |
使用 |
我们建议尽可能将属性存储为小写短横线格式,例如my.person.first-name=Rod 。 |
绑定映射
绑定到Map
属性时,您可能需要使用特殊的括号表示法才能保留原始key
值。如果键没有用[]
括起来,则会删除任何非字母数字、-
或.
字符。
例如,考虑将以下属性绑定到Map<String,String>
-
属性
-
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(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"}
条目。
合并复杂类型
当在多个位置配置列表时,覆盖是通过替换整个列表来完成的。
例如,假设一个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()
}
考虑以下配置
-
属性
-
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
时,将使用具有最高优先级(且仅使用该一个)的配置文件。考虑以下示例
-
属性
-
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()
}
考虑以下配置
-
属性
-
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
)。
上述合并规则适用于所有属性源的属性,而不仅仅是文件。 |
属性转换
Spring Boot 会尝试在绑定到@ConfigurationProperties
bean 时将外部应用程序属性强制转换为正确的类型。如果您需要自定义类型转换,您可以提供一个ConversionService
bean(bean 名称为conversionService
)或自定义属性编辑器(通过CustomEditorConfigurer
bean)或自定义Converters
(使用标注为@ConfigurationPropertiesBinding
的 bean 定义)。
由于此 bean 在应用程序生命周期的早期被请求,因此请确保限制您的ConversionService 使用的依赖项。通常,您需要的任何依赖项在创建时可能尚未完全初始化。如果您的自定义ConversionService 不是必需的配置键强制转换,并且仅依赖于使用@ConfigurationPropertiesBinding 限定的自定义转换器,则您可能需要重命名您的自定义ConversionService 。 |
转换持续时间
Spring Boot 专为表达持续时间提供了支持。如果您公开一个java.time.Duration
属性,则可以使用应用程序属性中的以下格式
-
常规的
long
表示法(除非指定了@DurationUnit
,否则使用毫秒作为默认单位) -
标准 ISO-8601 格式 由
java.time.Duration
使用 -
更易读的格式,其中值和单位耦合在一起(
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
都等效。可以以下列任何形式指定 500 毫秒的读取超时: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 )。这样做可以提供透明的升级路径,同时支持更丰富的格式。 |
转换周期
除了持续时间外,Spring Boot 还可以与java.time.Period
类型一起使用。可以在应用程序属性中使用以下格式
-
常规的
int
表示法(除非指定了@PeriodUnit
,否则使用天作为默认单位) -
标准 ISO-8601 格式 由
java.time.Period
使用 -
更简单的格式,其中值和单位对耦合在一起(
1y3d
表示 1 年和 3 天)
使用简单格式支持以下单位
-
y
表示年 -
m
表示月 -
w
表示周 -
d
表示天
java.time.Period 类型实际上从未存储周数,它是一个表示“7 天”的快捷方式。 |
转换数据大小
Spring Framework 有一个DataSize
值类型,它以字节为单位表示大小。如果您公开一个DataSize
属性,则可以使用应用程序属性中的以下格式
-
常规的
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 的@Validated
注解注释@ConfigurationProperties
类时,Spring Boot 都会尝试验证这些类。您可以直接在配置类上使用 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。将您的 Web 浏览器指向/actuator/configprops 或使用等效的 JMX 端点。有关详细信息,请参阅生产就绪功能部分。 |
@ConfigurationProperties 与 @Value
@Value
注解是一个核心容器功能,它不提供与类型安全配置属性相同的特性。下表总结了@ConfigurationProperties
和@Value
支持的功能
功能 | @ConfigurationProperties |
@Value |
---|---|---|
是 |
有限的(参见下面的注释) |
|
是 |
否 |
|
|
否 |
是 |
如果您确实想要使用 例如, |
如果您为自己的组件定义了一组配置键,我们建议您将它们分组在一个使用@ConfigurationProperties
注解的 POJO 中。这样做将为您提供结构化、类型安全的对象,您可以将其注入到您自己的 bean 中。
来自应用程序属性文件的SpEL
表达式在解析这些文件和填充环境时不会被处理。但是,可以在@Value
中编写SpEL
表达式。如果来自应用程序属性文件的属性值是SpEL
表达式,则在通过@Value
使用时将对其进行评估。