7.2. 外部化配置
Spring Boot 允许您外部化您的配置,以便您可以在不同的环境中使用相同的应用程序代码。您可以使用各种外部配置源,包括 Java 属性文件、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)的属性。命令行参数。
测试的
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 上下文初始化参数来代替环境变量或系统属性,或者与环境变量或系统属性一起使用。
为了提供一个具体的示例,假设您开发一个@Component
使用name
属性的应用程序,如以下示例所示:
在应用程序类路径上(例如,在 jar 内),您可以有一个 application.properties
文件,它为name
提供合理的默认属性值。在新环境中运行时,可以在 jar 外部提供application.properties
文件来覆盖name
。对于一次性测试,您可以使用特定的命令行开关启动(例如java -jar app.jar --name="Spring"
)。
env
和configprops
端点可用于确定属性具有特定值的原因。您可以使用这两个端点来诊断意外的属性值。有关详细信息,请参阅“生产就绪功能”部分。
7.2.1. 访问命令行属性
默认情况下,SpringApplication
将任何命令行选项参数(即以 --
开头的参数,例如--server.port=9000
)转换为property
并将它们添加到 Spring Environment
。如前所述,命令行属性始终优先于基于文件的属性源。
如果您不希望将命令行属性添加到Environment
中,可以使用 SpringApplication.setAddCommandLineProperties(false)
禁用它们。
7.2.2. JSON 应用程序属性
环境变量和系统属性通常有限制,这意味着某些属性名称无法使用。为了解决这个问题,Spring Boot 允许您将属性块编码到单个 JSON 结构中。
当您的应用程序启动时,任何spring.application.json
或SPRING_APPLICATION_JSON
属性都将被解析并添加到Environment
.
例如,可以在 UN*X shell 的命令行上将SPRING_APPLICATION_JSON
属性作为环境变量提供:
在前面的示例中,my.name=test
最终会进入Spring Environment
。
还可以提供相同的 JSON 作为系统属性:
或者您可以使用命令行参数提供 JSON:
如果要部署到经典应用程序服务器,您还可以使用名为java:comp/env/spring.application.json
的 JNDI 变量。
尽管JSON 中的null
值将添加到生成的属性源中,但PropertySourcesPropertyResolver
会将null
属性视为缺失值。这意味着 JSON 无法使用null
值覆盖来自低阶属性源的属性。
7.2.3. 外部应用程序属性
当您的应用程序启动时,Spring Boot 将自动从以下位置查找并加载application.properties
文件:application.yaml
从类路径
类路径根
类路径
/config
包
从当前目录
当前目录
config/
当前目录下的子目录config/
子目录的直接子目录
该列表按优先级排序(较低项目的值覆盖较早项目的值)。已加载文件中的文档将作为PropertySources
添加到 Spring Environment
中。
如果您不喜欢application
作为配置文件名,可以通过指定 spring.config.name
环境属性切换到其他文件名。例如,要查找 myproject.properties
和 myproject.yaml
文件,您可以按如下方式运行应用程序:
您还可以使用spring.config.location
环境属性来引用显式位置。此属性接受以逗号分隔的一个或多个要检查位置的列表。
以下示例显示如何指定两个不同的文件:
如果位置是可选的并且您不介意它们不存在, 请使用
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-.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
选择的任何其他基本名称)中为应用程序提供默认值。然后可以在运行时使用位于自定义位置之一的不同文件覆盖这些默认值。
Optional Locations
默认情况下,当指定的配置数据位置不存在时,Spring Boot 将抛出ConfigDataLocationNotFoundException
异常,并且您的应用程序将无法启动。
如果您想指定一个位置,但不介意它并不总是存在,则可以使用optional:
前缀。您可以将此前缀与spring.config.location
和spring.config.additional-location
属性以及spring.config.import
声明一起使用。
例如,即使myconfig.properties
文件丢失,optional:file:./myconfig.properties
中的spring.config.import
值也允许您的应用程序启动。
如果您想忽略所有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
中的值覆盖。
最后获胜策略适用于位置组级别。
spring.config.location
的classpath:/cfg/,classpath:/ext/
不会具有与classpath:/cfg/;classpath:/ext/
相同的覆盖规则。例如,继续上面的 prod,live 示例,我们可能有以下文件:
当我们有一个
spring.config.location
的classpath:/cfg/,classpath:/ext/
时,我们会在所有/ext
文件之前处理所有/cfg
文件:
/cfg/application-live.properties
/ext/application-prod.properties
/ext/application-live.properties
当我们使用
classpath:/cfg/;classpath:/ext/
(带有;
分隔符)时,我们会在同一级别处理/cfg
和/ext
:
/ext/application-prod.properties
/cfg/application-live.properties
/ext/application-live.properties
Environment
有一组默认配置文件(默认情况下为[default]
),如果未设置active
配置文件,则使用这些配置文件。换句话说,如果没有显式激活配置文件,则考虑来自application-default
的属性。
属性文件仅加载一次。如果您已经直接导入了配置文件特定的属性文件,则不会再次导入。
导入附加数据
应用程序属性可以使用spring.config.import
属性从其他位置导入更多配置数据。进口在发现时进行处理,并被视为紧邻插入进口申报文件下方的附加文件。
例如,您的类路径application.properties
文件中可能包含以下内容:
这将触发当前目录中dev.properties
文件的导入(如果存在这样的文件)。dev.properties
导入的值将优先于触发导入的文件。在上面的示例中,dev.properties
可以重新定义spring.application.name
为不同的值。
一次导入无论声明多少次,都只会导入一次。在properties/yaml 文件中的单个文档中定义导入的顺序并不重要。例如,下面的两个示例产生相同的结果:
在上述两个示例中,文件中的值my.properties
将优先于触发其导入的文件。
可以在一个键下指定多个位置spring.config.import
。位置将按照定义的顺序进行处理,稍后导入的优先。
在适当的情况下,还会考虑导入特定于配置文件的变体。上面的示例将导
my.properties
以及任何my-<profile>.properties
变体。Spring Boot 包含可插入 API,允许支持各种不同的位置地址。默认情况下,您可以导入 Java Properties、YAML 和“配置树”。
第三方 jar 可以提供对其他技术的支持(不要求文件位于本地)。例如,您可以想象配置数据来自外部存储,例如
Consul、Apache ZooKeeper
或Netflix Archaius
。如果您想支持自己的位置,请参阅
org.springframework.boot.context.config
包中的ConfigDataLocationResolver
和ConfigDataLoader
类。
导入无扩展名文件
某些云平台无法为卷安装的文件添加文件扩展名。要导入这些无扩展名文件,您需要给 Spring Boot 一个提示,以便它知道如何加载它们。您可以通过将扩展提示放在方括号中来完成此操作。
例如,假设您有一个/etc/config/myconfig
文件希望导入为 yaml。您可以在application.properties
使用以下命令将其导入:
使用配置树
在云平台(例如 Kubernetes)上运行应用程序时,您通常需要读取平台提供的配置值。将环境变量用于此类目的并不罕见,但这可能有缺点,特别是如果该值应该保密的话。
作为环境变量的替代方案,许多云平台现在允许您将配置映射到已安装的数据卷中。例如,Kubernetes 可以对ConfigMaps
和Secrets
进行卷挂载。
可以使用两种常见的卷安装模式:
单个文件包含一组完整的属性(通常编写为 YAML)。
多个文件被写入目录树,文件名成为“键”,内容成为“值”。
对于第一种情况,您可以使用上述spring.config.import
方法直接导入 YAML 或 Properties 文件。对于第二种情况,您需要使用configtree:
前缀,以便 Spring Boot 知道它需要将所有文件公开为属性。
作为示例,我们假设 Kubernetes 已安装以下卷:
文件的内容username
将是一个配置值,并且 password
的内容将是一个加密内容。
要导入这些属性,您可以将以下内容添加到您的application.properties
或application.yaml
文件中:
然后,您可以按照通常的方式从环境中访问或注入 myapp.username
和myapp.password
属性。
配置树下的文件夹和文件的名称构成属性名称。在上面的示例中,要访问用户名和密码属性,您可以将
spring.config.import
设置为optional:configtree:/etc/config/myapp
。带点符号的文件名也可以正确映射。例如,在上面的示例中,
/etc/config
中名为myapp.username
的文件将在环境中生成myapp.username
属性。配置树值可以根据预期的内容 绑定到字符串
String
和byte[]
类型。
如果您有多个配置树要从同一父文件夹导入,则可以使用通配符快捷方式。任何configtree:
以/*/
结尾的位置都将导入所有直接子项作为配置树。与非通配符导入一样,每个配置树下的文件夹和文件的名称形成属性名称。
例如,给定以下卷:
您可以用作configtree:/etc/config/*/
导入位置:
这将添加db.username
、db.password
、mq.username
和mq.password
属性。
使用通配符加载的目录按字母顺序排序。如果您需要不同的订单,那么您应该将每个位置列为单独的导入
配置树也可用于 Docker 秘密。当 Docker swarm 服务被授予对机密的访问权限时,该机密就会被安装到容器中。例如,如果将名为db.password
的机密安装在 /run/secrets/
,您可以使用以下命令将db.password
提供给 Spring 环境:
属性占位符
application.properties
和application.yaml
中的值在使用时会通过Environment
现有值进行过滤,因此您可以引用以前定义的值(例如,来自系统属性或环境变量的值)。标准${name}
属性占位符语法可以在值内的任何位置使用。属性占位符还可以使用 :
将默认值与属性名称分隔开来指定默认值,例如${name:default}
。
以下示例显示了带默认值和不带默认值的占位符的使用:
假设username
属性尚未在其他地方设置,app.description
则值为MyApp is a Spring Boot application written by Unknown
。
您应该始终使用规范形式(仅使用小写字母的短横线大小写)来引用占位符中的属性名称。这将允许 Spring Boot 使用与宽松绑定 @ConfigurationProperties 时相同的逻辑。
例如,
${demo.item-price}
将从application.properties
文件中获取demo.item-price
和demo.itemPrice
表单,以及从系统环境中获取EMO_ITEMPRICE
。如果您使用${demo.itemPrice}
代替,则不会考虑demo.item-price
和DEMO_ITEMPRICE
。您还可以使用此技术创建现有 Spring Boot 属性的“短”变体。有关详细信息,请参阅使用“短”命令行参数操作方法。
处理多文档文件
Spring Boot 允许您将单个物理文件拆分为多个逻辑文档,每个逻辑文档都是独立添加的。文件按从上到下的顺序处理。后面的文档可以覆盖前面文档中定义的属性。
对于application.yaml
文件,使用标准 YAML 多文档语法。三个连续的连字符代表一个文档的结束和下一个文档的开始。
例如,以下文件有两个逻辑文档:
对于application.properties
文件,使用特殊#---
或!---
注释来标记文档拆分:
属性文件分隔符不得包含任何前导空格,并且必须恰好包含三个连字符。分隔符之前和之后的行不能是相同的注释前缀。
多文档属性文件通常与 spring.config.activate.on-profile 等激活属性结合使用。有关详细信息,请参阅下一节。
无法使用
@PropertySource
或@TestPropertySource
注释加载多文档属性文件。
激活属性文件
有时,仅在满足某些条件时激活一组给定的属性很有用。例如,您可能拥有仅在特定配置文件处于活动状态时才相关的属性。
您可以使用 有条件地激活属性文档spring.config.activate.*
。
以下激活属性可用:
属性 | 备注 |
| 必须匹配才能使文档处于活动状态的配置文件表达式。 |
|
|
例如,以下内容指定第二个文档仅在 Kubernetes 上运行时才处于活动状态,并且仅当“prod”或“staging”配置文件处于活动状态时:
7.2.4. 加密属性
Spring Boot 不提供任何对加密属性值的内置支持,但是,它确实提供了修改 Spring Environment
中包含的值所需的挂钩点。EnvironmentPostProcessor
接口允许您在应用程序启动之前操作Environment
。有关详细信息,请参阅在启动之前自定义环境或 ApplicationContext 。
如果您需要一种安全的方式来存储凭据和密码,Spring Cloud Vault项目提供了在HashiCorp Vault中存储外部化配置的支持。
7.2.5. 使用 YAML
YAML是 JSON 的超集,因此是指定分层配置数据的便捷格式。只要您的类路径上有SnakeYAML库,SpringApplication
就会自动支持 YAML 作为属性的替代方案。
如果您使用“Starters”,SnakeYAML 会自动由
spring-boot-starter
提供.
将 YAML 映射到属性文件
YAML 文档需要从分层格式转换为可与 Spring 一起使用的平面结构Environment
。例如,考虑以下 YAML 文档:
为了从Environment
访问这些属性,它们将被展平如下:
同样,YAML 列表也需要扁平化。它们表示为具有[index]
解引用器的属性键。例如,考虑以下 YAML:
前面的示例将转换为以下属性:
使用 [index] 表示法的属性可以使用 Spring Boot 的 Binder 类绑定到
Java List
或Set
对象。有关更多详细信息,请参阅下面的“类型安全配置属性”部分。无法使用
@PropertySource
或@TestPropertySource
注释 加载 YAML 文件。因此,如果您需要以这种方式加载值,则需要使用属性文件。
直接加载YAML
Spring框架提供了两个方便的类,可用于加载YAML文档。YamlPropertiesFactoryBean
将YAML 加载为Properties
,YamlMapFactoryBean
将YAML 加载为Map
.
如果您想将 YAML 作为 Spring PropertySource
加载,您也可以使用YamlPropertySourceLoader
类。
7.2.6. 配置随机值
RandomValuePropertySource
对于注入随机值(例如,注入秘密或测试用例)非常有用。它可以生成整数、长整型、uuid 或字符串,如以下示例所示:
random.int*
语法是 OPEN value (,max) CLOSE
,其中OPEN、CLOSE
是任意字符,value、max
是整数。如果提供了max
,则 value
是最小值,max
是最大值(不包括)。
7.2.7. 配置系统环境属性
Spring Boot 支持为环境属性设置前缀。如果系统环境由具有不同配置要求的多个 Spring Boot 应用程序共享,这非常有用。系统环境属性的前缀可以直接在 SpringApplication
上设置。
例如,如果将前缀设置为input
,则诸如remote.timeout
之类的属性也将像在系统环境中一样被作为input.remote.timeout
解析。
7.2.8. 类型安全的配置属性
使用@Value("${property}")
注释来注入配置属性有时可能很麻烦,特别是当您正在使用多个属性或您的数据本质上是分层的时。Spring Boot 提供了另一种使用属性的方法,让强类型 bean 管理和验证应用程序的配置。
另请参阅类型安全配置属性和类型安全配置属性之间的差异@Value
。
JavaBean 属性绑定
可以绑定声明标准 JavaBean 属性的 bean,如以下示例所示:
前面的 POJO 定义了以下属性:
my.service.enabled
,默认值为false
。my.service.remote-address
,具有可以从String
强转的类型。my.service.security.username
,带有一个嵌套的“安全”对象,其名称由属性名称确定。特别是,该类型根本没有在那里使用,并且本来可以使用SecurityProperties
。my.service.security.password
。my.service.security.roles
,其中的String
集合默认为USER
.
映射到 Spring Boot 中可用的
@ConfigurationProperties
类的属性(通过属性文件、YAML 文件、环境变量和其他机制进行配置)是公共 API,但类本身的访问器(getter/setter)并不意味着可以直接使用。这种安排依赖于默认的空构造函数,并且 getter 和 setter 通常是强制性的,因为绑定是通过标准 Java Beans 属性描述符进行的,就像在 Spring MVC 中一样。在以下情况下可以省略 setter:
集合,只要它们被初始化,就需要一个 getter,但不一定需要一个 setter,因为它们可以被绑定器改变。
可以通过索引(通常使用 YAML)或使用单个逗号分隔值(属性)来访问的集合和数组。在后一种情况下,setter 是强制性的。我们建议始终为此类类型添加 setter。如果初始化集合,请确保它不是不可变的(如前面的示例所示)。
如果嵌套 POJO 属性已初始化(如
Security
前面示例中的字段),则不需要 setter。如果您希望绑定器使用其默认构造函数动态创建实例,则需要一个设置器。有些人使用 Project Lombok 自动添加 getter 和 setter。确保 Lombok 不会为此类类型生成任何特定的构造函数,因为容器会自动使用它来实例化对象。最后,仅考虑标准 Java Bean 属性,并且不支持静态属性的绑定。
构造函数绑定
上一节中的示例可以以不可变的方式重写,如下例所示:
在此设置中,单个参数化构造函数的存在意味着应使用构造函数绑定。这意味着绑定器将找到一个带有您希望绑定的参数的构造函数。如果您的类有多个构造函数,则可以使用@ConstructorBinding
注释来指定用于构造函数绑定的构造函数。要选择退出具有单个参数化构造函数的类的构造函数绑定,该构造函数必须使用@Autowired
进行注释。构造函数绑定可以与记录一起使用。除非您的记录有多个构造函数,否则无需使用@ConstructorBinding
.
构造函数绑定类的嵌套成员(例如Security
上面的示例)也将通过其构造函数进行绑定。
可以使用构造函数的@DefaultValue
参数和记录组件指定默认值。转换服务将用于将注释的String
值强制转换为缺失属性的目标类型。
参考前面的示例,如果没有属性绑定到Security
,则MyProperties
实例将包含具有null
值得security
对象。要使Security
包含非空实例,即使没有绑定任何属性(使用 Kotlin 时,这将要求username
和password
参数Security
声明为可为空,因为它们没有默认值),请使用空注释@DefaultValue
:
要使用构造函数绑定,必须使用
@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。
启用@ConfigurationProperties注释的类型
Spring Boot 提供了绑定@ConfigurationProperties
类型并将它们注册为 bean 的基础设施。您可以逐类启用配置属性,也可以启用与组件扫描类似的配置属性扫描。
有时,带有@ConfigurationProperties
注释的类可能不适合扫描,例如,如果您正在开发自己的自动配置或想要有条件地启用它们。在这些情况下,请使用@EnableConfigurationProperties
注释指定要处理的类型列表。这可以在任何@Configuration
类上完成,如以下示例所示:
要使用配置属性扫描,请将@ConfigurationPropertiesScan
注释添加到您的应用程序中。通常,它被添加到带有@SpringBootApplication
注释的主应用程序类中,但它可以添加到任何@Configuration
类中。默认情况下,将从声明注释的类的包中进行扫描。如果要定义要扫描的特定包,可以按照以下示例所示进行操作:
当使用配置属性扫描或通过
@EnableConfigurationProperties
注册@ConfigurationProperties
bean时,该bean具有常规名称:<prefix>-<fqn>
,其中<prefix>
是@ConfigurationProperties
注释中指定的环境键前缀,<fqn>
是 bean 的完全限定名称。如果注释不提供任何前缀,则仅使用 bean 的完全限定名称。假设它在
com.example.app
包中,上面的SomeProperties
示例的bean名称是some.properties-com.example.app.SomeProperties
。
我们建议@ConfigurationProperties
只处理环境,特别是不要从上下文中注入其他 bean。对于极端情况,可以使用 setter 注入或框架提供的任何*Aware
接口(例如EnvironmentAware
,如果您需要访问Environment
)。如果您仍然想使用构造函数注入其他 bean,则配置属性 bean 必须使用 @Component
进行注释并使用基于 JavaBean 的属性绑定。
使用@ConfigurationProperties注释的类型
这种配置风格特别适用于SpringApplication
的外部 YAML 配置,如以下示例所示:
要使用@ConfigurationProperties
Bean,您可以按照与任何其他 Bean 相同的方式注入它们,如以下示例所示:
使用
@ConfigurationProperties
还可以让您生成元数据文件,IDE 可以使用这些文件来为您自己的密钥提供自动完成功能。详细内容 请参见附录。
第三方配置
@ConfigurationProperties
除了用于注释类之外,您还可以在公共的@Bean
方法上使用它。当您想要将属性绑定到您无法控制的第三方组件时,这样做特别有用。
要从Environment
属性配置 bean,请添加@ConfigurationProperties
到其 bean 注册中,如以下示例所示:
使用another
前缀定义的任何 JavaBean 属性都会以与前面的 SomeProperties
示例类似的方式映射到 AnotherComponent
bean。
宽松的绑定
Spring Boot 使用一些宽松的规则将Environment
属性绑定到@ConfigurationProperties
bean,因此Environment
属性名称和 bean 属性名称之间不需要完全匹配。此功能有用的常见示例包括用破折号分隔的环境属性(例如,context-path
绑定到contextPath
)和大写的环境属性(例如,PORT
绑定到port
)。
作为示例,请考虑以下@ConfigurationProperties
类:
通过上面的代码,可以使用以下属性名称:
属性 | 备注 |
| Kebab case,建议在 |
| 标准驼峰式语法。 |
|
|
| 大写格式,在使用系统环境变量时建议使用大写格式。 |
注释的prefix
值必须采用短横线大小写(小写并用-
分隔,例如my.main-project.person
)。
属性来源 | 单个命名 | 列表 |
属性文件 | 驼峰式大小写、烤肉串大小写或下划线表示法 | 使用 |
YAML 文件 | 驼峰式大小写、烤肉串大小写或下划线表示法 | 标准 YAML 列表语法或逗号分隔值 |
环境变量 | 大写格式,下划线作为分隔符(请参阅从环境变量绑定)。 | 用下划线包围的数值(请参阅从环境变量绑定) |
系统属性 | 驼峰式大小写、烤肉串大小写或下划线表示法 | 使用 |
我们建议,如果可能,属性以小写短横线格式存储,例如my.person.first-name=Rod
.
绑定Map
绑定到Map
属性时,您可能需要使用特殊的括号表示法,以便保留原始key
值。如果键没有被 []
包围,则任何非字母数字字符-
或.
都会被删除。
例如,考虑将以下属性绑定到 Map<String,String>
:
对于 YAML 文件,括号需要用引号括起来,以便正确解析键。
上面的属性将绑定到Map
,/key1
、/key2
、key3
作为映射中的键。斜杠已被删除,key3
因为它没有被方括号包围。
绑定到标量值时,带有.
其中不需要用[]
包围。标量值包括枚举和 java.lang
包中除 Object 之外的所有类型。将 a.b=c
绑定到 Map 将保留 .
并返回一个包含条目{"a.b"="c"}
的 Map。对于任何其他类型,如果您的键包含.
,则需要使用方括号表示法。例如,将 a.b=c
绑定到 Map 将返回一个带有条目 {"a"={"b"="c"}}
的 Map 而[a.b]=c
将返回带有条目{"a.b"="c"}
的 Map。
从环境变量绑定
大多数操作系统对可用于环境变量的名称施加严格的规则。例如,Linux shell 变量只能包含字母 ( a
toz
或A
to Z
)、数字 ( 0
to 9
) 或下划线字符 ( _
)。按照惯例,Unix shell 变量的名称也采用大写形式。
Spring Boot 宽松的绑定规则旨在尽可能兼容这些命名限制。
要将规范形式的属性名称转换为环境变量名称,您可以遵循以下规则:
将点 (
.
) 替换为下划线 (_
)。删除所有破折号 (
-
)。转换为大写。
例如,配置属性spring.main.log-startup-info
将是名为 SPRING_MAIN_LOGSTARTUPINFO
的环境变量。
绑定到对象列表时也可以使用环境变量。要绑定到List
,变量名称中的元素编号应用下划线括起来。
例如,配置属性my.service[0].other
将使用名为 MY_SERVICE_0_OTHER
的环境变量。
缓存
宽松绑定使用缓存来提高性能。默认情况下,此缓存仅应用于不可变属性源。要自定义此行为,例如启用可变属性源的缓存,请使用ConfigurationPropertyCaching
.
合并复杂类型
当在多个位置配置列表时,覆盖通过替换整个列表来进行。
例如,假设一个MyPojo
对象具有默认属性null
的name
和description
。以下示例公开了来自 MyPojo
的对象列表MyProperties
:
考虑以下配置:
如果dev
配置文件未激活,MyProperties.list
则包含一个MyPojo
条目(如先前所定义)。但是,如果dev
配置文件已启用,则list
仍仅包含一个条目(名称为my another name
,描述为null
)。此配置不会将第二个MyPojo
实例添加到列表中,也不会合并项目。
当在多个配置文件中指定List
时,将使用具有最高优先级的配置文件(且仅使用该配置文件)。考虑以下示例:
在前面的示例中,如果dev
配置文件处于活动状态,MyProperties.list
则包含一个 MyPojo
条目(名称为my another name
,描述为null
)。对于 YAML,逗号分隔列表和 YAML 列表都可用于完全覆盖列表的内容。
对于Map
属性,您可以绑定从多个源获取的属性值。但是,对于多个源中的同一属性,将使用优先级最高的属性。以下示例公开了 MyProperties
中的Map<String, MyPojo>
:
考虑以下配置:
如果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(带有名为 conversionService
的 bean)或自定义属性编辑器(通过CustomEditorConfigurer
bean)或自定义Converters
(带有注释为@ConfigurationPropertiesBinding
的 bean 定义)。
由于此 bean 在应用程序生命周期的早期就被请求,因此请确保限制您的
ConversionService
正在使用的依赖项。通常,您需要的任何依赖项在创建时可能不会完全初始化。如果配置键强制不需要并且仅依赖于@ConfigurationPropertiesBinding
限定的自定义转换器,您可能需要重命名自定义ConversionService
。
转换持续时间
Spring Boot 专门支持表达持续时间。如果您公开java.time.Duration
属性,则应用程序属性中的以下格式可用:
正则
long
表示(除非指定了@DurationUnit
,否则使用毫秒作为默认单位)java.time.Duration
使用的标准 ISO-8601 格式一种更易读的格式,其中值和单位耦合(
10s
表示 10 秒)
考虑以下示例:
要指定 30 秒的会话超时,30
、PT30S
和30s
都是等效的。可以通过以下任意形式指定 500ms 的读取超时:500
、PT0.5S
和500ms
。
您还可以使用任何受支持的单位。这些都是:
ns
纳秒us
微秒ms
毫秒s
秒m
分钟h
小时d
天
默认单位是毫秒,可以使用@DurationUnit
上面示例中所示的方法进行覆盖。
如果您更喜欢使用构造函数绑定,则可以公开相同的属性,如以下示例所示:
如果您要升级Long
属性,请确保定义单位(使用@DurationUnit
)(如果它不是毫秒)。这样做提供了透明的升级路径,同时支持更丰富的格式。
转换期间
除了持续时间之外,Spring Boot 还可以使用java.time.Period
类型。应用程序属性中可以使用以下格式:
常规
int
表示(使用天作为默认单位,除非@PeriodUnit
已指定 a)使用的标准 ISO-8601 格式
java.time.Period
一种更简单的格式,其中值和单位对耦合(
1y3d
表示 1 年零 3 天)
简单格式支持以下单位:
y
年m
月w
周d
天
该
java.time.Period
类型实际上从不存储周数,它是一个快捷方式,意思是“7 天”。
转换数据大小
Spring框架有一个以DataSize
字节为单位表示大小的值类型。如果您公开DataSize
属性,则应用程序属性中的以下格式可用:
正则
long
表示(除非指定了@DataSizeUnit
,否则使用字节作为默认单位)一种更易读的格式,其中值和单位耦合(
10MB
表示 10 兆字节)
考虑以下示例:
指定缓冲区大小为10兆字节,10
与10MB
是等价的。256 字节的大小阈值可以指定为256
或256B
。
您还可以使用任何受支持的单位。这些都是:
B
字节KB
千字节MB
兆字节GB
千兆字节TB
兆兆字节
默认单位是字节,可以使用@DataSizeUnit
上面示例中所示的方法进行覆盖。
如果您更喜欢使用构造函数绑定,则可以公开相同的属性,如以下示例所示:
如果要升级
Long
属性,请确保定义单位(使用@DataSizeUnit
)(如果它不是字节)。这样做提供了透明的升级路径,同时支持更丰富的格式。
@ConfigurationProperties 验证
每当使用 Spring @Validated
的注释对类进行注释时,Spring Boot 就会尝试验证@ConfigurationProperties
类。您可以直接在配置类上使用 JSR-303约定的jakarta.validation
注释。为此,请确保您的类路径上有兼容的 JSR-303 实现,然后向您的字段添加约束注释,如以下示例所示:
您还可以通过使用@Validated
注释来对创建配置属性的@Bean
方法来触发验证。
为了确保即使没有找到任何属性,也始终会触发嵌套属性的验证,关联字段必须使用@Valid
进行注释。以下示例基于前面的MyProperties
示例:
您还可以通过创建名为Validator
的 bean 定义来添加自定义 Spring configurationPropertiesValidator
。@Bean
方法应该被声明static
。配置属性验证器是在应用程序生命周期的早期创建的,并且将该@Bean
方法声明为静态可以创建 bean,而无需实例化@Configuration
类。这样做可以避免早期实例化可能引起的任何问题。
spring-boot-actuator
模块包括一个公开所有@ConfigurationProperties
bean 的端点。将您的 Web 浏览器指向/actuator/configprops
或使用等效的 JMX 端点。有关详细信息,请参阅“生产就绪功能”部分。
@ConfigurationProperties 与 @Value
@Value
注解是核心容器功能,它不提供与类型安全配置属性相同的功能。下表总结了@ConfigurationProperties
和@Value
支持的功能:
如果您确实想使用
@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
在解析这些文件并填充环境时,不会处理应用程序属性文件中的表达式 。但是,可以SpEL
在 中编写表达式@Value
。如果应用程序属性文件中的属性值是一个SpEL
表达式,则在通过@Value
.
最后更新于