20220511 Core Features - 2. Externalized Configuration
前言
Spring Boot 使您可以将配置外部化,以便可以在不同环境中使用相同的应用程序代码。您可以使用 Properties文件,YAML文件,环境变量 和 命令行参数 来外部化配置。
属性值可以通过 @Value
注解直接注入到你的 bean ,通过 Spring Environment
抽象 的访问,或者通过 @ConfigurationProperties
绑定到结构化对象
Spring Boot 使用一个非常特殊的 PropertySource
顺序,该顺序旨在允许合理地覆盖值。属性按以下顺序考虑,优先级从低到高,数字更大的配置会覆盖更小的:
- 默认属性(通过设置
SpringApplication.setDefaultProperties
指定) @Configuration
类上的@PropertySource
注解。请注意,在刷新应用程序上下文之前,不会将此类属性源添加到Environment
中。配置某些属性(例如在刷新开始之前读取的logging.*
和spring.main.*
)为时已晚- 配置数据(例如
application.properties
文件) - 仅在
random.*
中具有属性的RandomValuePropertySource
- 操作系统环境变量
- Java 系统属性,
System.getProperties()
- 来自
java:comp/env
的 JNDI 属性 ServletContext
初始化参数ServletConfig
初始化参数- 来自
SPRING_APPLICATION_JSON
(嵌入在环境变量或系统属性中的内联 JSON )的属性 - 命令行参数
- tests 中的
properties
属性。可用于@SpringBootTest
注解和测试应用程序的特定部分的测试注解 - tests 中的
@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
和.yml
格式的配置文件,则.properties
优先
举一个具体的例子,假设你开发了一个使用 name
属性的 @Component
,如下例所示:
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class MyBean {
@Value("${name}")
private String name;
// ...
}
在您的应用程序类路径(例如,在您的 jar 中)上,有一个 application.properties
文件,该文件为 name
提供一个默认值。在新环境中运行时,可以在 jar 外提供一个 application.properties
文件,该文件覆盖 name
属性。对于一次性测试,您可以使用特定的命令行开关启动(例如 java -jar app.jar --name="Spring"
)
env
和configprops
端点可用于确定属性为何具有特定值。您可以使用这两个端点来诊断意外的属性值。有关详细信息,请参阅 生产就绪功能 部分。
2.1. 访问命令行属性
默认情况下,SpringApplication
将任何命令行选项 (option) 参数(即以 --
开头的参数,例如 --server.port=9000
)转换为 property
并将它们添加到 Spring Environment
。如前所述,命令行属性始终优先于基于文件的属性源。
如果您不想将命令行属性添加到 Environment
中,可以使用 SpringApplication.setAddCommandLineProperties(false)
禁用它们
2.2. 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
值覆盖来自低阶属性源的属性
2.3. 外部应用程序属性
当您的应用程序启动时,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
中指定的文件直接导入。
目录和文件位置值也被扩展以检查 特定于 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_CONFIG_NAME
代替spring.config.name
)。有关详细信息,请参阅 从环境变量绑定
如果您的应用程序在 servlet 容器或应用程序服务器中运行,则可以使用 JNDI 属性(在
java:comp/env
中)或 servlet 上下文初始化参数来代替环境变量或系统属性,或者也可以使用环境变量或系统属性。
2.3.1. 可选位置(Optional Locations)
默认情况下,当指定的配置数据位置不存在时,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
。
2.3.2. 通配符位置(Wildcard Locations)
如果配置文件位置包含最后一个路径段的字符 *
,则将其视为通配符位置。加载配置时会扩展通配符,以便同时检查直接子目录。当有多个配置属性来源时,通配符位置在 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:
位置中使用通配符。
2.3.3. 特定 profile 配置文件
除了 application
属性文件,Spring Boot 还将尝试使用命名约定 application-{profile}
加载特定于 profile 的文件。例如,如果您的应用程序激活了一个名为 prod
的 profile 并使用 YAML 文件,那么 application.yml
和 application-prod.yml
两者都会被考虑。
特定于 profile 的属性从与标准 application.properties
相同的位置加载,特定于 profile 的文件总是覆盖非特定文件。如果指定了多个 profile ,则应用 最后获胜( last-wins )策略 。例如,如果 spring.profiles.active
属性指定 profile 为 prod,live
由,则 application-prod.properties
中的值可以被 application-live.properties
中的值覆盖。
最后获胜的策略适用于 位置组 级别。
spring.config.location
为classpath:/cfg/,classpath:/ext/
不会具有与classpath:/cfg/;classpath:/ext/
相同的覆盖规则。例如,继续上面
prod,live
的示例,我们可能有以下文件:/cfg application-live.properties /ext application-live.properties application-prod.properties
当我们设置
spring.config.location
为classpath:/cfg/,classpath:/ext/
时,我们在所有/ext
文件之前处理所有/cfg
文件
/cfg/application-live.properties
/ext/application-prod.properties
/ext/application-live.properties
当我们设置
spring.config.location
为classpath:/cfg/;classpath:/ext/
时,我们在同一级别处理所有/ext
和/cfg
文件
/ext/application-prod.properties
/cfg/application-live.properties
/ext/application-live.properties
Environment
有一组默认 profile(默认情况下,[default]
),如果没有设置活动 profile ,则使用这些 profile 。换句话说,如果没有显式激活 profile ,则使用来自 application-default
的属性。
属性文件只加载一次。如果您已经直接 导入 了特定 profile 的属性文件,则不会再次导入它。
2.3.4. 导入额外数据
应用程序属性可以使用 spring.config.import
属性从其他位置导入更多配置数据。导入数据在发现时就被处理,并被视为紧接在声明导入的文件下方插入的附加文件。
例如,您的类路径 application.properties
文件中可能包含以下内容:
spring.application.name=myapp
spring.config.import=optional:file:./dev.properties
这将触发当前目录中 dev.properties
文件的导入(如果存在这个文件)。 dev.properties
导入的值将优先于触发导入的文件。在上面的示例中,dev.properties
可以重新定义 spring.application.name
为不同的值。
无论声明多少次,导入都只会导入一次。在 properties/yaml 文件中的单个文档中定义导入的顺序无关紧要。例如,下面的两个示例产生相同的结果:
spring.config.import=my.properties
my.property=value
my.property=value
spring.config.import=my.properties
在上述两个示例中,my.properties
文件中的值将优先于触发其导入的文件。
可以在一个 spring.config.import
键下指定多个位置。位置将按照定义的顺序进行处理,以后的导入优先级更高。
适当时,还会考虑导入 特定于 profile 的变体 。上面的示例将同时导入
my.properties
和任何my-<profile>.properties
变体。
Spring Boot 包含可插入的 API,允许支持各种不同的位置地址。默认情况下,您可以导入 Java 属性、YAML 和 配置树
第三方 jar 可以提供对其他技术的支持(不需要文件是本地的)。例如,您可以想象配置数据来自外部存储,例如 Consul、Apache ZooKeeper 或 Netflix Archaius
如果您想支持自己的位置,请参阅
org.springframework.boot.context.config
包中的ConfigDataLocationResolver
和ConfigDataLoader
类。
2.3.5. 导入无扩展名文件
某些云平台无法为卷挂载文件添加文件扩展名。要导入这些无扩展名文件,您需要给 Spring Boot 一个提示,以便它知道如何加载它们。您可以通过将扩展提示放在方括号中来做到这一点。
例如,假设您有一个 /etc/config/myconfig
文件要导入为 yaml 。您可以使用以下命令从您的 application.properties
导入它:
spring.config.import=file:/etc/config/myconfig[.yaml]
2.3.6. 使用配置树
在云平台(例如 Kubernetes)上运行应用程序时,您通常需要读取平台提供的配置值。将环境变量用于此类目的并不少见,但这可能有缺点,尤其是在值应该保密的情况下。
作为环境变量的替代方案,许多云平台现在允许您将配置映射到挂载的数据卷。例如,Kubernetes 可以同时卷挂载 ConfigMaps
和 Secrets
可以使用两种常见的卷挂载模式:
- 单个文件包含一组完整的属性(通常写为 YAML)
- 多个文件被写入目录树,文件名成为“键”,内容成为“值”
对于第一种情况,您可以使用 上述 spring.config.import
方法直接导入 YAML 或属性文件。对于第二种情况,您需要使用 configtree:
前缀,以便 Spring Boot 知道它需要将所有文件公开为属性。
例如,假设 Kubernetes 挂载了以下卷:
etc/
config/
myapp/
username
password
username
文件的内容将是一个配置值,password
的内容将是一个 secret
要导入这些属性,您可以将以下内容添加到您的 application.properties
或 application.yaml
文件中:
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/*/
作导入位置:
spring.config.import=optional:configtree:/etc/config/*/
这将添加 db.username
、db.password
、mq.username
、mq.password
属性。
使用通配符加载的目录按字母顺序排序。如果您需要不同的顺序,则应将每个位置列为单独的导入
配置树也可用于 Docker secrets 。当 Docker swarm 服务被授予对 secrets 的访问权限时,该 secret 将被挂载到容器中。例如,如果一个名为 db.password
的 secret 挂载在 /run/secrets/
位置,您可以使用以下命令将 db.password
提供给 Spring 环境:
spring.config.import=optional:configtree:/run/secrets/
2.3.7. 属性中的占位符
application.properties
和 application.yml
中的值在使用时会通过现有 Environment
进行过滤,因此您可以参考以前定义的值(例如,来自系统属性或环境变量)。标准的 ${name}
属性占位符语法可以在任何地方使用。属性占位符还可以使用 :
来指定默认值,以将默认值与属性名称分开,例如 ${name:default}
以下示例显示了使用带和不带默认值的占位符:
app.name=MyApp
app.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 属性的“短”变体。有关详细信息,请参阅 Use ‘Short’ Command Line Arguments
2.3.8. 使用多文档文件
Spring Boot 允许您将单个物理文件拆分为多个独立添加的逻辑文档。文档从上到下按顺序处理。后面的文档可以覆盖更早文档中定义的属性。
对于 application.yml
文件,使用标准的 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
注解加载多文档属性文件
2.3.9. 激活属性
有时仅在满足某些条件时才激活给定的一组属性很有用。例如,您可能拥有仅在特定 profile 处于活动状态时才相关的属性。
您可以使用 spring.config.activate.*
有条件地激活属性文档。
可以使用以下激活属性:
属性 | 描述 |
---|---|
on-profile |
要使文档处于活动状态,必须匹配的 profile 表达式 |
on-cloud-platform |
要使文档处于活动状态,必须检测到 CloudPlatform |
例如,以下内容指定第二个文档仅在 Kubernetes 上运行时才处于活动状态,并且仅当 prod
或 staging
profile 处于活动状态时:
myprop=always-set
#---
spring.config.activate.on-cloud-platform=kubernetes
spring.config.activate.on-profile=prod | staging
myotherprop=sometimes-set
2.4. 加密属性
Spring Boot 不提供对加密属性值的任何内置支持,但是,它确实提供了修改 Spring Environment
中包含的值所需的挂钩点。EnvironmentPostProcessor
接口允许您在应用程序启动之前进行操作 Environment
。有关详细信息,请参阅 Customize the Environment or ApplicationContext Before It Starts
如果您需要一种安全的方式来存储凭据和密码,Spring Cloud Vault 项目支持在 HashiCorp Vault 中存储外部化配置。
2.5. 使用 YAML
YAML 是 JSON 的超集,因此是一种用于指定分层配置数据的便捷格式。只要您的类路径中有 SnakeYAML 库,SpringApplication
类就会自动支持 YAML 作为 properties 的替代方案。
如果您使用启动器,SnakeYAML 会自动由
spring-boot-starter
引入
2.5.1. 将 YAML 映射到 Properties
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
类绑定到 JavaList
或Set
。有关更多详细信息,请参阅 类型安全配置属性
YAML 文件不能使用
@PropertySource
或@TestPropertySource
注解加载。因此,如果您需要以这种方式加载值,则需要使用 properties 文件。
直接加载 YAML
Spring Framework 提供了两个方便的类,可用于加载 YAML 文档。YamlPropertiesFactoryBean
加载 YAML 为 Properties
,YamlMapFactoryBean
加载 YAML 为 Map
如果您想将 YAML 作为 Spring PropertySource
加载,也可以使用 YamlPropertySourceLoader
类
2.6. 配置随机值
RandomValuePropertySource
对于注入随机值很有用(例如,注入密钥或测试用例)。它可以生成整数、长整数、uuid 或字符串,如下例所示:
my.secret=${random.value}
my.number=${random.int}
my.bignumber=${random.long}
my.uuid=${random.uuid}
my.number-less-than-ten=${random.int(10)}
my.number-in-range=${random.int[1024,65536]}
random.int*
语法是 OPEN value (,max) CLOSE
, OPEN,CLOSE
是任何字符,value,max
是整数。如果提供 max
,则 value
为最小值并且 max
为最大值(不包括)。
2.7. 配置系统环境属性
Spring Boot 支持为环境属性设置前缀。如果系统环境由具有不同配置要求的多个 Spring Boot 应用程序共享,这将很有用。系统环境属性的前缀可以直接由 SpringApplication
设置
例如,如果您将前缀设置为 input
,则 remote.timeout
也将被解析为系统环境中的 input.remote.timeout
属性
2.8. 类型安全的配置属性
使用 @Value("${property}")
注解注入配置属性有时会很麻烦,尤其是当您使用多个属性或您的数据本质上是分层的时候。Spring Boot 提供了一种使用属性的替代方法,可以让强类型 bean 管理和验证应用程序的配置。
2.8.1. JavaBean 属性绑定
可以绑定声明标准 JavaBean 属性的 bean,如下例所示:
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@ConfigurationProperties("my.service")
@Data
public class MyProperties {
private boolean enabled;
private InetAddress remoteAddress;
private final Security security = new Security();
@Data
public static class Security {
private String username;
private String password;
private List<String> roles = new ArrayList<>(Collections.singleton("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
映射到
@ConfigurationProperties
类的属性在 Spring Boot 中可用,通过属性文件、YAML 文件、环境变量和其他机制配置,是公共 API,但并不意味着直接使用类本身的访问器(getter/setter)
这种安排依赖于默认的空构造函数,并且 getter 和 setter 通常是强制性的,因为绑定是通过标准 Java Beans 属性描述符(property
descriptors)进行的,就像在 Spring MVC 中一样。在以下情况下可以省略 setter :
- Maps ,只要它们被初始化,需要 getter 但不一定是需要 setter,因为它们可以被 binder 改变
- 可以通过索引(通常使用 YAML)或使用单个逗号分隔值(属性)来访问集合和数组。在后一种情况下,setter 是强制性的。我们建议始终为此类类型添加 setter 。如果您初始化一个集合,请确保它不是不可变的
- 如果嵌套 POJO 属性已初始化(如前面示例中的
Security
字段),则不需要 setter 。如果您希望 binder 使用其默认构造函数动态创建实例,则需要一个 setter。有些人使用 Project Lombok 来自动添加 getter 和 setter。确保 Lombok 不会为此类类型生成任何特定的构造函数,因为容器会自动使用它来实例化对象。
最后,只考虑标准 Java Bean 属性,不支持对静态属性的绑定。
yml配置:
acme:
enabled: true
remoteAddress: 10.1.1.2
security:
username: abc
password: xxx
roles:
- a
- b
- c
- x
参考说明
使用 @ConfigurationProperties
注解,IDEA
出现警告信息:
解决方法:加入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
作用:点击自定义配置时,可以跳转到 Java 类上,也可以在编写配置文件时进行提示,但是提示功能要再运行一次程序。
2.8.2. 构造函数绑定
上一节中的示例可以以不可变的方式重写,如下例所示:
import java.net.InetAddress;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConstructorBinding;
import org.springframework.boot.context.properties.bind.DefaultValue;
@ConstructorBinding
@ConfigurationProperties("my.service")
public class MyProperties {
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;
}
public boolean isEnabled() {
return this.enabled;
}
public InetAddress getRemoteAddress() {
return this.remoteAddress;
}
public Security getSecurity() {
return this.security;
}
public static class Security {
private final String username;
private final String password;
private final List<String> roles;
public Security(String username, String password, @DefaultValue("USER") List<String> roles) {
this.username = username;
this.password = password;
this.roles = roles;
}
public String getUsername() {
return this.username;
}
public String getPassword() {
return this.password;
}
public List<String> getRoles() {
return this.roles;
}
}
}
在此设置中,@ConstructorBinding
注解指示应使用构造函数绑定。这意味着绑定器将期望找到具有您希望绑定的参数的构造函数。如果您使用的是 Java 16 或更高版本,则可以将构造函数绑定与记录(records)一起使用。在这种情况下,除非您的记录有多个构造函数,否则无需使用 @ConstructorBinding
@ConstructorBinding
类的嵌套成员(例如上面示例中的 Security
)也将通过其构造函数进行绑定。
可以使用 @DefaultValue
指定默认值,或者在使用 Java 16 或更高版本时,使用记录组件指定默认值。应用转换服务将 String
值强制为缺失属性的目标类型。
参考前面的示例,如果没有属性绑定到 Security
,则 MyProperties
实例将包含 security
的 null
值。如果您希望即使没有绑定任何属性也返回一个非空 Security
实例,您可以使用空 @DefaultValue
注解来执行此操作:
public MyProperties(boolean enabled, InetAddress remoteAddress, @DefaultValue Security security) {
this.enabled = enabled;
this.remoteAddress = remoteAddress;
this.security = security;
}
要使用构造函数绑定,必须使用
@EnableConfigurationProperties
或配置属性扫描启用类。您不能对通过常规 Spring 机制创建的 bean 使用构造函数绑定(例如,@Component
beans,通过@Bean
方法创建的 bean 或使用@Import
加载的 bean )
如果你的类有多个构造函数,你也可以直接在应该绑定的构造函数上使用
@ConstructorBinding
不推荐与
@ConfigurationProperties
一起使用java.util.Optional
,因为它主要用作返回类型。因此,它不太适合配置属性注入。为了与其他类型的属性保持一致,如果您确实声明了一个Optional
属性并且它没有值,将绑定null
而不是一个空的Optional
2.8.3. 启用 @ConfigurationProperties
注解的类
Spring Boot 提供了绑定 @ConfigurationProperties
类型并将其自动注册为 Bean 的基础架构。您可以按类启用配置属性,也可以启用与组件扫描类似的配置属性扫描。
有时,带 @ConfigurationProperties
注解的类可能不适合扫描,例如,如果您正在开发自己的自动配置或希望有条件地启用它们。在这些情况下,请使用 @EnableConfigurationProperties
注解指定要处理的类型列表。这可以在任何 @Configuration
类上完成,如下例所示:
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(SomeProperties.class)
public class MyConfiguration {
}
要使用配置属性扫描,请将 @ConfigurationPropertiesScan
注解添加到您的应用程序。通常,它被添加到带有 @SpringBootApplication
注解的主应用程序类中,但它可以添加到任何 @Configuration
类中。默认情况下,扫描将从声明注解的类的包进行。如果要定义要扫描的特定包,可以按照以下示例进行操作:
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
@SpringBootApplication
@ConfigurationPropertiesScan({ "com.example.app", "com.example.another" })
public class MyApplication {
}
当
@ConfigurationProperties
bean 使用配置属性扫描或通过@EnableConfigurationProperties
注册,bean 具有约定名称:<prefix>-<fqn>
,其中,<prefix>
是在@ConfigurationProperties
注解中指定的环境键前缀,<fqn>
是bean的全限定名。如果注解不提供任何前缀,则仅使用 bean 的完全限定名称。上例中的 bean 名称为
com.example.app-com.example.app.SomeProperties
我们建议 @ConfigurationProperties
取值只与 Environment
相关,尤其不要从上下文中注入其他 bean 。对于特殊情况,可以使用 setter 注入或框架提供的 *Aware
接口(例如,如果需要访问 Environment
,使用 EnvironmentAware
)。如果您仍想使用构造函数注入其他 bean ,则必须对配置属性 bean 使用 @Component
注解,并使用基于 JavaBean 的属性绑定。
2.8.4. 使用 @ConfigurationProperties
注解的类
这种配置风格特别适用于 SpringApplication
外部 YAML 配置,如下例所示:
my:
service:
remote-address: 192.168.1.1
security:
username: "admin"
roles:
- "USER"
- "ADMIN"
要使用 @ConfigurationProperties
bean,您可以像注入任何其他 bean 一样注入它们,如以下示例所示:
import org.springframework.stereotype.Service;
@Service
public class MyService {
private final SomeProperties properties;
public MyService(SomeProperties properties) {
this.properties = properties;
}
public void openConnection() {
Server server = new Server(this.properties.getRemoteAddress());
server.start();
// ...
}
// ...
}
使用
@ConfigurationProperties
还允许您生成元数据文件,IDE 可以使用这些文件为您自己的键提供自动完成功能。详情见 附录
2.8.5. 第三方配置
除了用 @ConfigurationProperties
注解类之外,您还可以在公共 @Bean
方法上使用它。当您想要将属性绑定到您无法控制的第三方组件时,这样做会特别有用。
要用 Environment
属性配置 bean,请添加 @ConfigurationProperties
到其 bean 注册,如以下示例所示:
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();
}
}
使用 another
前缀定义的任何 JavaBean 属性都以类似于前面 SomeProperties
示例的方式映射到 AnotherComponent
bean。
2.8.6. 宽松绑定
Spring Boot 使用一些宽松的规则将 Environment
属性绑定到 @ConfigurationProperties
Bean,因此 Environment
属性名称和 Bean 属性名称之间不需要完全匹配。常见示例包括破折号分隔的环境属性(例如,context-path
绑定到 contextPath
)和大写的环境属性(例如,PORT
绑定到 port
)。
示例:
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;
}
}
上述代码,可以使用以下属性名称:
宽松绑定
属性 | 注意 |
---|---|
my.main-project.person.first-name |
短横线式 (Kebab) ,建议在 .properties 和 .yml 文件中使用 |
my.main-project.person.firstName |
标准驼峰式 (Camel) 语法 |
my.main-project.person.first_name |
下划线 (Underscore) 表示法,是在 .properties 和 .yml 文件中使用的另一种格式 |
MY_MAINPROJECT_PERSON_FIRSTNAME |
大写格式,使用系统环境变量时建议使用 |
@ConfigurationProperties
的属性prefix
的值必须为短横线式(小写并用-
分隔,例如my.main-project.person
)。不管配置文件中是什么格式
每个属性源的宽松绑定规则
属性来源 | 简单值 | List |
---|---|---|
Properties 文件 | 驼峰式,短横线式或下划线式 | 使用 [ ] 或以逗号分隔的值的标准列表语法 |
YAML 文件 | 驼峰式,短横线式或下划线式 | 标准 YAML List 语法或逗号分隔的值 |
环境变量 | 以下划线作为分隔符的大写格式。_ 不应在属性名称中使用(请参阅 从环境变量绑定 ) |
下划线括起来的数值,例如 MY_ACME_1_OTHER = my.acme[1].other (请参阅 从环境变量绑定 ) |
系统属性 | 驼峰式,短横线式或下划线式 | 使用 [ ] 或以逗号分隔的值的标准列表语法 |
我们建议,属性以小写短横线式存储,例如
my.person.first-name=Rod
绑定 Map
绑定 Map
属性时,如果 key 包含除小写字母、数字字符或 -
之外的任何内容,则需要使用方括号表示法,以便保留原始值。如果键没有被 []
包围,则所有其他字符被删除。例如:
map:
"[/key1]": value1
"[/key2]": value2
/key3: value3
my.map.[/key1]=value1
my.map.[/key2]=value2
my.map./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
的环境变量
2.8.7. 合并复杂类型
当列表配置在多个位置时,覆盖通过替换整个列表来工作。
例如,假设一个 MyPojo
对象具有默认值为 null
的 name
和 description
属性。以下示例公开了来自 MyProperties
的 MyPojo
对象列表:
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;
}
}
考虑以下配置:
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
仍然只包含一个条目( name
为 my another name
,description
为null
)。此配置不会将第二个 MyPojo
实例添加到列表中,并且不会合并项目。
当在多个配置文件中指定 List
时,使用具有最高优先级的一个(并且仅使用那个)。考虑以下示例:
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
条目( name
为 my another name
,description
为 null
)。对于 YAML ,逗号分隔的列表和 YAML 列表都可用于完全覆盖列表的内容。
对于 Map
属性,您可以绑定从多个来源提取的属性值。但是,对于多个源中的相同属性,将使用具有最高优先级的属性。以下示例公开了来自 MyProperties
的 Map<String, MyPojo>
:
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;
}
}
考虑以下配置:
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
包含一个带有键 key1
的条目( name
为 my name 1
,description
为 my description 1
)。但是,如果启用了 dev
profile ,则 map
包含两个条目,键 key1
( name
为 dev name 1
,description
为my description 1
)和 键 key2
( name
为dev name 2
,description
为dev description 2
)。
上述合并规则适用于所有属性源的属性,而不仅仅是文件。
2.8.8. 属性转换
当 Spring Boot 绑定到 @ConfigurationProperties
bean 时,它试图将外部属性强制转换为正确的类型。如果您需要自定义类型转换,则可以提供一个 ConversionService
Bean(名为 conversionService
的 bean )或一个自定义属性编辑器(通过 CustomEditorConfigurer
Bean)或自定义 Converters
(具有 @ConfigurationPropertiesBinding
注解的 bean )。
由于在应用生命周期中非常早就请求了此 bean ,因此请确保限制
ConversionService
的依赖项。通常,您需要的任何依赖项可能在创建时未完全初始化。如果不需要配置键强制转换并且仅依赖具有限定符@ConfigurationPropertiesBinding
的自定义转换器,则您可能想重命名自定义ConversionService
转换 Durations
Spring Boot 为表达 durations 提供了专门的支持。如果定义了 java.time.Duration
属性,则配置文件中属性可用以下格式:
- 常规
long
表示形式(使用毫秒作为默认单位,除非已指定@DurationUnit
) java.time.Duration
使用的标准的ISO-8601
格式- 值和单位耦合的更易读的格式(例如,
10s
表示 10 秒)
考虑以下示例:
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);
public Duration getSessionTimeout() {
return this.sessionTimeout;
}
public void setSessionTimeout(Duration sessionTimeout) {
this.sessionTimeout = sessionTimeout;
}
public Duration getReadTimeout() {
return this.readTimeout;
}
public void setReadTimeout(Duration readTimeout) {
this.readTimeout = readTimeout;
}
}
要指定 30 秒的 sessionTimeout
,30
、PT30S
、30s
都等效。500毫秒的 readTimeout
可以以任何形式如下指定:500
,PT0.5S
和 500ms
也可以使用任何受支持的单位:
ns
纳秒us
微秒ms
毫秒s
秒m
分钟h
小时d
天
默认时间单位是毫秒,可以使用 @DurationUnit
注解指定时间单位。
如果您更喜欢使用构造函数绑定,则可以公开相同的属性,如下例所示:
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConstructorBinding;
import org.springframework.boot.context.properties.bind.DefaultValue;
import org.springframework.boot.convert.DurationUnit;
@ConfigurationProperties("my")
@ConstructorBinding
public class MyProperties {
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;
}
public Duration getSessionTimeout() {
return this.sessionTimeout;
}
public Duration getReadTimeout() {
return this.readTimeout;
}
}
如果您要升级
Long
属性,如果它不是毫秒,请确保定义单位(使用@DurationUnit
)。这样做提供了一个透明的升级路径,同时支持更丰富的格式。
转换 periods
除了 durations ,Spring Boot 还可以使用 java.time.Period
类型。以下格式可用于应用程序属性:
- 常规
int
表示(使用天作为默认单位,除非已指定@PeriodUnit
) java.time.Period
使用 的标准 ISO-8601 格式- 一种更简单的格式,其中值和单位对耦合(
1y3d
表示 1 年和 3 天)
简单格式支持以下单位:
y
- 年m
- 月w
- 周d
- 天
java.time.Period
类型从不实际存储周数,它是一个表示7 天
的快捷方式。
转换数据大小(Data Sizes)
Spring Framework 的 DataSize
值类型表示字节大小。如果定义 DataSize
属性,则配置文件中属性可用以下格式:
- 常规
long
表示形式(除非已指定@DataSizeUnit
,否则使用byte
作为默认单位) - 值和单位耦合的更具可读性的格式(例如,
10MB
意味着 10 兆字节)
考虑以下示例:
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);
public DataSize getBufferSize() {
return this.bufferSize;
}
public void setBufferSize(DataSize bufferSize) {
this.bufferSize = bufferSize;
}
public DataSize getSizeThreshold() {
return this.sizeThreshold;
}
public void setSizeThreshold(DataSize sizeThreshold) {
this.sizeThreshold = sizeThreshold;
}
}
指定 10 MB 的 bufferSize
,10
、10MB
等效。可以将 256 个字节的 sizeThreshold
指定为 256
或 256B
。
也可以使用任何受支持的单位:
B
- 字节(byte)KB
- 千字节(K byte)MB
- 兆字节(M byte)GB
- 千兆字节(G byte)TB
- 太字节(T byte)
默认单位是 字节(byte)
,可以使用 @DataSizeUnit
指定单位。
如果您更喜欢使用构造函数绑定,则可以公开相同的属性,如下例所示:
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConstructorBinding;
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")
@ConstructorBinding
public class MyProperties {
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;
}
public DataSize getBufferSize() {
return this.bufferSize;
}
public DataSize getSizeThreshold() {
return this.sizeThreshold;
}
}
如果要升级
Long
属性,如果它不是字节(byte),请确保定义单位(使用@DataSizeUnit
)。这样做提供了一个透明的升级路径,同时支持更丰富的格式。
2.8.9. @ConfigurationProperties
验证
Spring Boot 会验证带有 @Validated
注解的 @ConfigurationProperties
类。您可以直接在配置类上使用 JSR-303
javax.validation
约束注解。为此,请确保在类路径上有兼容的 JSR-303 实现,然后将约束注解添加到字段上,如以下示例所示:
import java.net.InetAddress;
import javax.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;
public InetAddress getRemoteAddress() {
return this.remoteAddress;
}
public void setRemoteAddress(InetAddress remoteAddress) {
this.remoteAddress = remoteAddress;
}
}
还可以通过使用
@Validated
注解@Bean
方法来触发验证。
尽管绑定时也会验证嵌套属性,但最好在嵌套属性上使用 @Valid
注解,这样可确保即使未找到嵌套属性也将触发验证。下面的示例基于前面的 MyProperties
示例:
import java.net.InetAddress;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.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();
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;
public String getUsername() {
return this.username;
}
public void setUsername(String username) {
this.username = username;
}
}
}
您还可以通过创建名为 configurationPropertiesValidator
的bean定义来添加自定义 Spring Validator
。@Bean
方法应声明 static
。配置属性验证器是在应用生命周期的早期创建的,并且将 @Bean
方法声明为 static
可以使 bean 得以创建而不必实例化 @Configuration
类。这样做避免了由早期实例化可能引起的问题。
spring-boot-actuator
模块包括一个公开所有 @ConfigurationPropertiesbean
的功能。将您的 Web 浏览器指向 /actuator/configprops
或使用等效的 JMX 端点。需要添加依赖并配置属性:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
# 开启所有Endpoint
management.endpoints.web.exposure.include=*
2.8.10. @ConfigurationProperties
与 @Value
@Value
注解是核心容器的功能,它不提供和类型安全配置属性相同的功能。下表总结了@ConfigurationProperties
和 @Value
支持的功能:
功能 | @ConfigurationProperties | @Value |
---|---|---|
宽松绑定 | 是 | 有限 |
元数据支持 | 是 | 否 |
SpEL 表达式 | 否 | 是 |
元数据支持,就是从配置文件跳转到 Java 文件,还有编写配置文件时的提示功能。
如果您确实想使用
@Value
,我们建议您使用其规范形式(短横线,仅使用小写字母)引用属性名称。这将允许 Spring Boot 使用与宽松绑定@ConfigurationProperties
时相同的逻辑。例如,@Value("{demo.item-price}")
将从application.properties
文件中提取demo.item-price
和demo.itemPrice
,以及系统环境的DEMO_ITEMPRICE
。如果你改用了@Value("{demo.itemPrice}")
,就不会支持demo.item-price
和DEMO_ITEMPRICE
如果您为自己的组件定义了一组配置键,我们建议您将它们组合到一个 POJO 中,并用 @ConfigurationProperties
注解。这样做将为您提供结构化的、类型安全的对象,您可以将其注入到您自己的 bean 中。
在解析这些文件和填充环境时,不会处理来自 应用属性文件 的 SpEL
表达式。但是,可以在 @Value
中编写 SpEL
表达式。如果应用属性文件中的属性值是一个 SpEL
表达式,则在通过 @Value
使用时将对其进行计算。