Externalized Configuration

Spring Boot 允许你外部化配置,以便可以在不同的环境中使用相同的应用程序代码。你可以使用各种外部配置源,包括 Java 属性文件、YAML 文件、环境变量和命令行参数。 可以使用 @Value 批注将属性值直接注入到 Bean 中,通过 Spring 的 Environment 抽象访问,或通过 @ConfigurationPropertiesbound to structured objects。 Spring Boot 使用了一个非常特殊的 PropertySource 顺序,该顺序旨在允许合理地覆盖值。后面的属性源可以覆盖前面定义的值。源按以下顺序考虑:

  1. 默认属性(通过设置 SpringApplication.setDefaultProperties 指定)。

  2. @Configuration 类上的 {url-spring-framework-javadoc}/org/springframework/context/annotation/PropertySource.html[@PropertySource] 批注。请注意,这些属性源在刷新应用程序上下文之前不会添加到 Environment 中。对于某些属性(例如 logging. and spring.main.),这是在刷新开始之前读取的,所以为时已晚。

  3. 配置数据(例如 application.properties 文件)。

  4. RandomValuePropertySource 只有在 random.* 中才有属性。

  5. OS environment variables.

  6. Java System properties (System.getProperties()).

  7. JNDI attributes from java:comp/env.

  8. ServletContext init parameters.

  9. ServletConfig init parameters.

  10. SPRING_APPLICATION_JSON 中的属性(嵌入在环境变量或系统属性中的内联 JSON)。

  11. Command line arguments.

  12. 测试上的 properties 属性。可在 @SpringBootTesttest annotations for testing a particular slice of your application 上使用。

  13. 在测试中使用 {url-spring-framework-javadoc}/org/springframework/test/context/DynamicPropertySource.html[@DynamicPropertySource] 注释。

  14. 在测试中使用 {url-spring-framework-javadoc}/org/springframework/test/context/TestPropertySource.html[@TestPropertySource] 注释。

  15. 当 devtools 处于活动状态时,在 $HOME/.config/spring-boot 目录中的 Devtools global settings properties

配置数据文件按以下顺序考虑:

  1. 打包在 jar 中的 Application properties (`application.properties`和 YAML 变体)。

  2. 打包在 jar 中的 Profile-specific application properties (`application-{profile}.properties`和 YAML 变体)。

  3. 打包在 jar 外部的 Application properties (`application.properties`和 YAML 变体)。

  4. 打包在 jar 外部的 Profile-specific application properties (`application-{profile}.properties`和 YAML 变体)。

建议在整个应用程序中使用一种格式。如果您在同一位置含有 `.properties`和 YAML 格式的配置文件,则 `.properties`优先。

如果您使用环境变量而不是系统属性,则大多数操作系统不允许使用以句点分隔的键名,不过您可以改用下划线(例如,configprop:spring.config.name[format=envvar] 而不是 configprop:spring.config.name[])。有关详细信息,请参见 Binding From Environment Variables

如果您的应用程序在 servlet 容器或应用程序服务器中运行,则 JNDI 属性(在 `java:comp/env`中)或 servlet 上下文初始化参数可用于代替环境变量或系统属性,或者与它们一起使用。

为了提供一个具体的示例,假设您开发一个 @Component,该 @Component`使用一个 `name`属性,如下例所示: include-code::MyBean[] 在应用程序类路径中(例如,在 jar 内部),您可以有一个 `application.properties`文件,该文件为 `name`提供明智的默认属性值。在新的环境中运行时,可以在 jar 外部提供一个 `application.properties`文件,该文件覆盖 `name。对于一次性测试,您可以使用特定命令行开关启动(例如,java -jar app.jar --name="Spring")。

`env`和 `configprops`端点可用于确定属性具有特定值的原因。您可以使用这两个端点来诊断意外的属性值。有关详细信息,请参见“Production ready features”部分。

Accessing Command Line Properties

默认情况下,SpringApplication`将任何命令行选项参数(即以 `--`开头的参数,例如 `--server.port=9000)转换为一个 property,然后将它们添加到 Spring Environment。如前所述,命令行属性始终优先于基于文件的属性源。

如果您不希望命令行属性添加到 Environment,则可以使用 `SpringApplication.setAddCommandLineProperties(false)`禁用它们。

JSON Application Properties

环境变量和系统属性通常有受限,这意味着无法使用某些属性名称。为了解决此问题,Spring Boot 允许您将属性块编码到一个 JSON 结构中。

当您的应用程序启动时,所有 spring.application.json`或 `SPRING_APPLICATION_JSON`属性都将被解析并添加到 `Environment

例如,`SPRING_APPLICATION_JSON`属性可在 UN*X shell 中作为环境变量在命令行上提供:

$ SPRING_APPLICATION_JSON='{"my":{"name":"test"}}' java -jar myapp.jar

在下例中,最终得到 Spring 中的 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 值将被添加到结果属性源中,但 PropertySourcesPropertyResolvernull 属性当作缺少值。这意味着 JSON 无法使用 null 值覆盖低阶属性源中的属性。

External Application Properties

应用程序启动时,Spring Boot 将自动查找并加载以下位置中的 application.propertiesapplication.yaml 文件:

  1. From the classpath[style="loweralpha"]

    1. The classpath root

    2. The classpath /config package

  2. From the current directory[style="loweralpha"]

    1. The current directory

    2. 当前目录中的 config/ 子目录

    3. config/ 子目录的直接子目录

该列表按优先级排序(较低项中的值覆盖较早项)。加载的文件中的文档以 PropertySources 形式添加到 Spring Environment 中。

如果不希望将 application 作为配置文件的名称,可以通过指定 configprop:spring.config.name[] 环境属性切换到其他文件名。例如,要查找 myproject.propertiesmyproject.yaml 文件,可以按如下方式运行应用程序:

$ java -jar myproject.jar --spring.config.name=myproject

还可以通过使用 configprop:spring.config.location[] 环境属性引用显式位置。该属性接受一个或多个要检查位置的逗号分隔列表。

以下示例显示如何指定两份不同的文件:

$ java -jar myproject.jar --spring.config.location=\
	optional:classpath:/default.properties,\
	optional:classpath:/override.properties

如果存在 locations are optional ,可以使用前缀 optional: ,而不在意是否存在。

非常早地使用 spring.config.namespring.config.locationspring.config.additional-location 来确定要加载哪些文件。必须将它们定义为环境属性(通常是操作系统环境变量、系统属性或命令行参数)。

如果 spring.config.location 包含目录(而不是文件),它们应以 / 结尾。在加载之前,将在运行时将它们追加到从 spring.config.name 生成的名称。spring.config.location 中指定的文件会被直接导入。

目录和文件位置值都会被展开以检查 profile-specific files 。例如,如果 spring.config.location 的值为 classpath:myconfig.properties,那么还会发现适当的 classpath:myconfig-<profile>.properties 文件已加载。

在大多数情况下,添加的每个 configprop:spring.config.location[] 项都会引用单个文件或目录。位置按指定的顺序进行处理,后面的位置可以覆盖前面的位置。

如果有复杂的位置设置,并且使用特定于配置文件,则可能需要提供更多提示,以便 Spring Boot 知道应该如何将它们分组。位置组是同一级别所有位置的集合。例如,可能希望对所有类路径位置进行分组,然后对所有外部位置进行分组。位置组中的项应以 ; 分隔。有关更多详细信息,请参阅 “Profile Specific Files” 部分中的示例。

使用 spring.config.location 配置的位置会替换默认位置。例如,如果将 spring.config.location 配置为值 optional:classpath:/custom-config/,optional:file:./custom-config/,那么考虑的位置的完整集合是:

  1. optional:classpath:custom-config/

  2. optional:file:./custom-config/

如果你希望添加其他位置,而不是替换它们,则可以使用 spring.config.additional-location。从其他位置加载的属性可以覆盖默认位置中的属性。例如,如果配置的 spring.config.additional-location 值为 optional:classpath:/custom-config/,optional:file:./custom-config/,考虑的位置的完整集合为:

  1. optional:classpath:/;optional:classpath:/config/

  2. optional:file:./;optional:file:./config/;optional:file:./config/*/

  3. optional:classpath:custom-config/

  4. optional:file:./custom-config/

此搜索顺序允许你在一个配置文件中指定默认值,然后在另一个配置文件中选择性重写这些值。你可以在默认位置之一中(或使用 spring.config.name 选择的任何其他基本名称)的 application.properties(或使用 spring.config.name 选择的任何其他基本名称)中为你的应用程序提供默认值。然后可以使用位于自定义位置之一中的不同文件在运行时覆盖这些默认值。

Optional Locations

默认情况下,当指定的配置数据位置不存在时,Spring Boot 将抛出一个 ConfigDataLocationNotFoundException,并且你的应用程序将不会启动。

如果你想指定一个位置,但你不介意它是否一直存在,则可以使用 optional: 前缀。你可以在 spring.config.locationspring.config.additional-location 属性以及 spring.config.import 声明中使用此前缀。

例如,即使不存在 myconfig.properties 文件,spring.config.import 值为 optional:file:./myconfig.properties 也可以允许你的应用程序启动。

如果你想要忽略所有 ConfigDataLocationNotFoundExceptions 并始终继续启动你的应用程序,你可以使用 spring.config.on-not-found 属性。使用 SpringApplication.setDefaultProperties(…​) 或通过系统/环境变量将值设置为 ignore

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.locationspring.config.additional-location 属性使用通配符位置。

通配符位置对于作为目录的搜索位置只包含一个 and end with /,或者对于作为文件的搜索位置只包含一个 */<filename> 是必需的。具有通配符的位置根据文件名的绝对路径按字母顺序排序。

通配符位置仅适用于外部目录。你不能在 classpath: 位置中使用通配符。

Profile Specific Files

除了 application 属性文件之外,Spring Boot 还会尝试使用命名约定 application-{profile} 加载特定于概要的文件。例如,如果你的应用程序激活了名为 prod 的概要并使用了 YAML 文件,那么 application.yamlapplication-prod.yaml 都将被考虑。

特定于概要的属性从与标准 application.properties 相同的位置加载,其中特定于概要的文件始终覆盖非特定于概要的文件。如果指定了多个概要,则应用“最近者优先”策略。例如,如果通过 configprop:spring.profiles.active[] 属性指定了概要 prod,live,则 application-prod.properties 中的值可以被 application-live.properties 中的值覆盖。

“最近者优先”策略在 location group 级别应用。classpath:/cfg/,classpath:/ext/ 的 configprop:spring.config.location[] 将不会具有与 classpath:/cfg/;classpath:/ext/ 相同的替代规则。 例如,在继续上述我们的 prod,live 示例时,我们可能拥有以下文件:

/cfg
  application-live.properties
/ext
  application-live.properties
  application-prod.properties

当我们有 configprop:spring.config.location[] 时,在所有 /ext 文件之前处理所有 /cfg 文件:

  1. /cfg/application-live.properties

  2. /ext/application-prod.properties

  3. /ext/application-live.properties

当我们反而有 classpath:/cfg/;classpath:/ext/(带有 ; 分隔符)时,在同一级别处理 /cfg/ext

  1. /ext/application-prod.properties

  2. /cfg/application-live.properties

  3. /ext/application-live.properties

如果未设置任何活动概要,Environment 有一组默认概要(默认情况下为 [default])。换言之,如果没有明确激活任何概要,则会考虑 application-default 中的属性。

属性文件只会加载一次。如果你已经直接 imported 了特定于概要的属性文件,则不会再次导入它。

Importing Additional Data

应用程序属性可以使用 spring.config.import 属性从其他位置导入更多配置数据。导入在被发现时处理,并被视为立即在声明导入的位置下方插入的附加文档。

例如,你可能在 classpath application.properties 文件中拥有以下内容:

spring:
  application:
    name: "myapp"
  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-specific variants 也考虑进行导入。上面的示例将导入 my.properties,以及任何 my-<profile>.properties 的变体。

Spring Boot 包括可插入 API,该 API 支持各种不同的位置地址。默认情况下,您可以导入 Java 属性、YAML 和 “configuration trees”。 第三方 jar 可以为额外的技术提供支持(文件不必是本地的,没有此项要求)。例如,您可以想象外部存储(例如 Consul、Apache ZooKeeper 或 Netflix Archaius)中的配置数据。 如果您想支持您自己的位置,请查看 org.springframework.boot.context.config 包中的 ConfigDataLocationResolverConfigDataLoader 类。

Importing Extensionless Files

一些云平台无法为已装载的文件添加文件扩展名。要导入这些没有扩展名的文件,你需要向 Spring Boot 提供一个提示,以便它知道如何加载它们。你可以通过在方括号中放置扩展名提示来执行此操作。

例如,假设你有希望导入为 yaml 的 s0 文件。你可以使用以下命令从 s1 导入它:

spring:
  config:
    import: "file:/etc/config/myconfig[.yaml]"

Using Configuration Trees

在云平台(例如 Kubernetes)上运行应用程序时,你通常需要读取平台提供的配置值。为此目的使用环境变量很常见,但这可能存在缺陷,尤其是在该值应保密时。

作为环境变量的替代方案,许多云平台现在允许你将配置映射到已装载的数据卷。例如,Kubernetes 可以卷装载 s4 和 s5。

可以使用两种常见的卷装载模式:

  1. 一个文件包含一组完整属性(通常写为 YAML)。

  2. 在目录树中写入多个文件,文件名变为“键”,内容变为“值”。

对于第一个案例,您可以直接使用 `spring.config.import`导入 YAML 或 Properties 文件,如 above中所述。对于第二个案例,您需要使用 `configtree:`前缀,以便 Spring Boot 知道它需要公开所有文件作为属性。

例如,让我们想象一下 Kubernetes 挂载了以下卷:

etc/
  config/
    myapp/
      username
      password

`username`文件的内容将是 config 值,而 `password`的内容将是机密。

要导入这些属性,您可以将以下内容添加到您的 `application.properties`或 `application.yaml`文件中:

spring:
  config:
    import: "optional:configtree:/etc/config/"

然后,您可以通过 `Environment`以通常的方式访问或注入 `myapp.username`和 `myapp.password`属性。

config 树下的文件夹和文件名称构成了属性名称。在上面的示例中,要以 username`和 `password`作为属性来访问这些属性,您可以将 `spring.config.import`设置为 `optional:configtree:/etc/config/myapp

带点表示法的文件名也正确地映射了。例如,在上面的示例中,`/etc/config`中的名为 `myapp.username`的文件将在 `Environment`中产生一个 `myapp.username`属性。

根据预期的内容,Configuration 树值可以绑定到字符串 `String`和 `byte[]`类型。

如果您有多个 config 树要从同一个父文件夹导入,可以使用通配符快捷方式。任何以 `/*/`结尾的 `configtree:`位置都将导入所有直接子级作为 config 树。与非通配符导入一样,每个 config 树下的文件夹和文件名称构成了属性名称。

例如,给定以下卷:

etc/
  config/
    dbconfig/
      db/
        username
        password
    mqconfig/
      mq/
        username
        password

您可以使用 `configtree:/etc/config/*/`作为导入位置:

spring:
  config:
    import: "optional:configtree:/etc/config/*/"

这将添加 db.usernamedb.password、`mq.username`和 `mq.password`属性。

使用通配符加载的目录按字母顺序排序。如果您需要不同的顺序,则应将每个位置列为单独的导入

Configuration 树也可以用于 Docker 机密。当 Docker 群集服务被授予访问机密的权限时,机密会被装载到容器中。例如,如果在位置 `/run/secrets/`上装载了一个名为 `db.password`的机密,则可以使用以下内容使 `db.password`可用于 Spring 环境:

spring:
  config:
    import: "optional:configtree:/run/secrets/"

Property Placeholders

application.properties`和 `application.yaml`中的值在使用时将通过现有的 `Environment`进行过滤,因此您可以返回到先前定义的值(例如,来自系统属性或环境变量)。标准 `${name}`属性占位符语法可以在值中的任何位置使用。属性占位符还可以使用 `:`指定默认值,以将默认值与属性名称分开,例如 `${name:default}

以下示例显示了带或不带默认值使用占位符的情况:

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 使用与处理 relaxed binding@ConfigurationProperties`时相同的逻辑。 例如,${demo.item-price}` 将从 application.properties 文件中选取 demo.item-pricedemo.itemPrice 表单,以及从系统环境中选取 DEMO_ITEMPRICE。如果使用 ${demo.itemPrice},将不会考虑 demo.item-priceDEMO_ITEMPRICE

还可以使用这种技术创建现有 Spring Boot 属性的 “short” 变体。请参阅 Use '`Short’ Command Line Arguments 了解详情。

Working With Multi-Document Files

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)一起使用。请参阅 next section 了解详情。

无法使用 @PropertySource@TestPropertySource 注释加载多文档属性文件。

Activation Properties

有时仅在满足特定条件时激活给定的属性集是很有用的。例如,可以具备仅在特定配置文件处于活动状态时才相关的属性。

可以使用 spring.config.activate.* 根据条件激活属性文档。

以下激活属性可用:

Table 1. activation properties
Property Note

on-profile

文档处于活动状态必须匹配的配置文件表达式。

on-cloud-platform

文档处于活动状态必须检测到的 CloudPlatform

例如,以下指定第二个文档仅在 Kubernetes 上运行时处于活动状态,并且仅当 “prod” 或 “staging” 配置文件处于活动状态时:

myprop:
  "always-set"
---
spring:
  config:
    activate:
      on-cloud-platform: "kubernetes"
      on-profile: "prod | staging"
myotherprop: "sometimes-set"

Encrypting Properties

Spring Boot 不提供任何内置支持来加密属性值,但是,它确实提供了必要的挂钩点来修改包含在 Spring Environment 中的值。在应用程序启动之前,EnvironmentPostProcessor 接口允许操作 Environment。有关详细信息,请参阅 Customize the Environment or ApplicationContext Before It Starts

如果需要安全的方式来存储凭证和密码, Spring Cloud Vault 项目将提供将外部配置存储在 HashiCorp Vault 中的支持。

Working With YAML

YAML 是 JSON 的超集,因此是用于指定层次配置数据的便捷格式。只要在类路径上拥有 SnakeYAML 库,SpringApplication 类便自动支持 YAML 作为属性的替代方案。

如果您使用 “Starters”, spring-boot-starter 将自动提供 SnakeYAML。

Mapping YAML to 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 类绑定到 Java ListSet 对象。有关更多详细信息,请参阅下面的 “Type-safe Configuration Properties” 部分。

YAML 文件不能使用 @PropertySource@TestPropertySource 注释加载。因此,如果您需要以这种方式加载值,则需要使用属性文件。

Directly Loading YAML

Spring Framework 提供了两个方便的类,可以用来加载 YAML 文档。YamlPropertiesFactoryBean 将 YAML 作为 Properties 加载,而 YamlMapFactoryBean 将 YAML 作为 Map 加载。

如果您想将 YAML 作为 Spring PropertySource 加载,您也可以使用 YamlPropertySourceLoader 类。

Configuring Random Values

RandomValuePropertySource 对于注入随机值(例如注入到机密或测试案例中)很有用。它可以生成整数、长整数、UUID 或字符串,如下例所示:

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 是最大值(不包括)。

Configuring System Environment Properties

Spring Boot 支持为环境属性设置前缀。如果系统环境由多个 Spring Boot 应用程序共享,并且这些应用程序具有不同的配置要求,此功能非常有用。系统环境属性的前缀可以在 SpringApplication 上直接设置。

例如,如果您将前缀设置为 input,则诸如 remote.timeout 的属性也会在系统环境中解析为 input.remote.timeout

Type-safe Configuration Properties

使用 @Value("${property}") 注释注入配置属性有时会很麻烦,尤其是在处理多属性或数据本质上是分层的情况下。Spring Boot 提供了一种处理属性的替代方法,该方法允许经过强类型处理的 Bean 管理和验证应用程序的配置。

JavaBean Properties Binding

可以绑定一个声明标准 JavaBean 属性的 Bean,如下例所示:

前述 POJO 定义了以下属性:

  • my.service.enabled,默认值为 false

  • my.service.remote-address,类型可以从 String 强制转换。

  • s6 ,其中嵌套的“安全性”对象由属性名称确定。特别是,该类型在那里根本没有使用,可能已被 s7 替代。

  • my.service.security.password.

  • s8,其中 s9 的集合默认为 s10。

映射到 Spring Boot 中可用的 s11 类的属性(通过属性文件、YAML 文件、环境变量和其他机制进行配置)是公开 API,但该类的访问器(getter/setter)本身不用于直接使用。

这种安排依赖于默认的空构造函数,而 getter 和 setter 通常是强制性的,因为绑定是通过标准 Java Beans 属性描述符进行的,就像在 Spring MVC 中一样。在以下情况下可以省略 setter:

  • 只要初始化即可,映射需要获取器,但不必有设置器,因为它们可通过绑定器变更。

  • 可以通过索引(通常使用 YAML)或使用单一的逗号分隔的值(属性)访问集合和数组。在后一种情况下,设置器是必需的。我们建议始终为这些类型添加一个设置器。如果您初始化了一个集合,请确保它不是不可变的(如前一个示例所示)。

  • 如果初始化了嵌套的 POJO 属性(如前一个示例中的 Security 字段),则不需要设置器。如果您希望绑定器使用其默认构造函数即时创建实例,则需要一个设置器。

有些人使用 Project Lombok 自动添加获取器和设置器。请确保 Lombok 不针对此类类型生成任何特定构造函数,因为容器会自动使用它实例化对象。 最后,只考虑标准 Java Bean 属性,并且不支持静态属性上的绑定。

Constructor Binding

前一节中的示例可以用不可变的方式改写,如下例所示:

在此设置中,存在一个单参数化构造函数暗示应使用构造函数绑定。这意味着绑定器将找到具有您希望绑定的参数的构造函数。如果您的类具有多个构造函数,则 @ConstructorBinding 注解可用于指定应针对构造函数绑定使用哪个构造函数。要选择不为具有单个参数化构造函数的类进行构造函数绑定,必须使用 @Autowired 注解构造函数。构造函数绑定可用于记录。除非记录有多个构造函数,否则无需使用 @ConstructorBinding

构造函数绑定类的嵌套成员(如上述示例中的 Security)也将通过其构造函数进行绑定。

可以使用 @DefaultValue 在构造函数参数和记录组件上指定默认值。转换服务将应用于强制转换批注的 String 值为缺少属性的目标类型。

参考前一个示例,如果没有任何属性绑定到 Security,则 MyProperties 实例将包含 securitynull 值。为了使其包含一个非空 Security 实例,即使没有任何属性绑定到它(当使用 Kotlin 时,这要求 Securityusernamepassword 参数被声明为可为空的,因为它们没有默认值),请使用 @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 将被绑定。

Enabling @ConfigurationProperties-annotated Types

Spring Boot 提供基础设施,用于绑定“@1”类型并将其注册为 bean。您可以逐个类启用配置属性,或启用与组件扫描工作方式类似的配置属性扫描。

有时,使用“@2”进行注释的类可能不适合扫描,例如,如果您正在开发自己的自动配置,或您希望有条件地启用它们。在这些情况下,请使用“@3”注释指定要使用该类型的列表。这可以在任何“@4”类上完成,如下例所示:

要使用配置属性扫描,请向您的应用程序添加“@5”注释。通常,它被添加到使用“@6”进行注释的主应用程序类,但它可以被添加到任何“@7”类。默认情况下,将从声明注释的类的包中进行扫描。如果您希望定义要扫描的特定包,可以按照如下示例进行操作:

当“@8”bean 使用配置属性扫描或通过“@9”注册时,bean 具有常规名称:“@10”,其中“@11”是在“@12”注释中指定的环境密钥前缀,“@13”是 bean 的完全限定名称。如果注释未提供任何前缀,则只使用 bean 的完全限定名称。 假设它在“@14”包中,则上面“@15”示例的 bean 名称为“@16”。

我们建议“@26”只处理环境,尤其是,不从上下文中注入其他 bean。对于特殊情况,可以使用 setter 注入或该框架提供的任何“@27”接口(例如,如果您需要访问“@29”,则可以使用“@28”)。如果您仍然希望使用构造函数注入其他 bean,则配置属性 bean 必须使用“@30”进行注释,并使用基于 JavaBean 的属性绑定。

Using @ConfigurationProperties-annotated Types

这种配置方式特别适合于“@31”外部 YAML 配置,如下例所示:

my:
  service:
    remote-address: 192.168.1.1
    security:
      username: "admin"
      roles:
      - "USER"
      - "ADMIN"

要使用“@32”bean,您可以像注入其他任何 bean 一样注入它们,如下例所示:

使用“@33”还允许您生成元数据文件,IDE 可使用这些文件为您的专属密钥提供自动完成功能。有关详细信息,请参见“@34”。

Third-party Configuration

除了使用“@35”对类进行注释外,您还可以在公有的“@36”方法上使用它。当您希望将属性绑定到不受您控制的第三方组件时,这样做特别有用。

要从“@37”属性配置 bean,请将其“@38”添加到其 bean 注册,如下例所示:

任何使用“@39”前缀定义的 JavaBean 属性都以类似于前面“@41”示例的方式映射到该“@40”bean。

Relaxed Binding

Spring Boot 使用一些宽松的规则将“@42”属性绑定到“@43”bean,因此“@44”属性名称和 bean 属性名称之间不必完全匹配。这方面有用的常见示例包括以破折号分隔的环境属性(例如,“@45”绑定到“@46”)和首字母大写的环境属性(例如,“@47”绑定到“@48”)。

例如,考虑以下“@49”类:

使用前面代码,可以使用以下所有属性名称:

Table 2. relaxed binding
Property Note

my.main-project.person.first-name

连字符形式,建议用于 .properties 和 YAML 文件中。

my.main-project.person.firstName

Standard camel case syntax.

my.main-project.person.first_name

下划线形式,作为替代格式使用在 .properties 和 YAML 文件中。

MY_MAINPROJECT_PERSON_FIRSTNAME

大写形式,使用系统环境变量时建议。

注释 mustprefix 值应为连字符形式(小写并由 - 分隔,例如 my.main-project.person)。

Table 3. relaxed binding rules per property source
Property Source Simple List

Properties Files

驼峰形式、连字符形式或下划线形式

使用 [ ] 或逗号分隔值的标准列表语法

YAML Files

驼峰形式、连字符形式或下划线形式

标准 YAML 列表语法或逗号分隔值

Environment Variables

大写形式,下划线作为分隔符(参见 Binding From Environment Variables)。

由下划线包围的数字值(参见 Binding From Environment Variables

System properties

驼峰形式、连字符形式或下划线形式

使用 [ ] 或逗号分隔值的标准列表语法

当可能时,我们建议按照小写的连字符形式存储属性,例如 my.person.first-name=Rod

Binding Maps

绑定到 Map 属性时,您可能需要使用特殊的花括号形式以便保留原始 key 值。如果密钥没有被 [] 所包围,则将移除任何非字母数字、-. 的字符。

例如,考虑绑定下面的属性到一个 Map<String,String>

my:
  map:
    "[/key1]": "value1"
    "[/key2]": "value2"
    "/key3": "value3"

对于 YAML 文件,为了正确解析密钥,花括号需要被引号包围。

上面的属性将绑定到一个 Map,其密钥为映射中的 /key1/key2key3。已从 key3 中删除斜杠,因为它没有被方括号包围。

绑定到标量值时,其中包含 . 的密钥不需要被 [] 所包围。标量值包括枚举值以及 java.lang 包中的所有类型(Object 除外)。将 a.b=c 绑定到 Map<String, String> 将保留密钥中的 .,并返回一个包含条目 {"a.b"="c"} 的映射。对于任何其他类型,如果您的 key 包含一个 .,则您需要使用花括号形式。例如,将 a.b=c 绑定到 Map<String, Object> 将返回一个包含条目 {"a"={"b"="c"}} 的映射,而 [a.b]=c 将返回一个包含条目 {"a.b"="c"} 的映射。

Binding From Environment Variables

大多数操作系统都针对可用于环境变量的名称制定了严格的规则。例如,Linux shell 变量只能包含字母(azAZ)、数字(09)或下划线字符 (_)。根据惯例,Unix shell 变量的名称还会采用大写。

Spring Boot 的宽松绑定规则最大限度地设计为与这些命名限制兼容。

要将规范形式中的属性名称转换为环境变量名称,您可以遵循这些规则:

  • 将点 (.) 替换为下划线 (_)。

  • Remove any dashes (-).

  • Convert to uppercase.

例如,配置属性 spring.main.log-startup-info 将是一个名为 SPRING_MAIN_LOGSTARTUPINFO 的环境变量。

在绑定到对象列表时也可以使用环境变量。要绑定到 List,应使用下划线在变量名称中包围元素编号。

例如,配置属性 my.service[0].other 将使用名为 MY_SERVICE_0_OTHER 的环境变量。

Caching

宽松绑定使用缓存来提高性能。默认情况下,此缓存仅应用于不可变属性源。例如,要自定义此行为,例如启用可变属性源的缓存,请使用 ConfigurationPropertyCaching

Merging Complex Types

当列表在多个地方配置时,覆盖通过替换整个列表起作用。

例如,假设一个 s12 对象具有 s13 和 s14 属性,它们在默认为 s15 的情况下更新。以下示例从 s17 揭示了 s16 对象的列表:

考虑以下配置:

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 still 仅包含一个条目(名称为 my another name,描述为 null)。此配置 does not 会将第二个 MyPojo 实例添加到列表,但不会合并项目。

当在多个配置文件中指定 List 时,会使用优先级最高的那个(并且仅使用该那个)。考虑以下示例:

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 包含 one MyPojo 条目(名称为 my another name,描述为 null)。对于 YAML,可以用逗号分隔的列表和 YAML 列表来完全覆盖列表的内容。

对于 Map 属性,您可以与从多个来源获取的属性值绑定。但是,对于多个来源中的同一属性,将使用优先级最高的那个。以下示例从 MyProperties 公开 Map<String, MyPojo>

考虑以下配置:

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)的条目。

上述合并规则应用于所有属性来源的属性,而不仅仅是文件。

Properties Conversion

Spring Boot 尝试在绑定到 @ConfigurationProperties Bean 时强制将外部应用程序属性转换为正确的类型。如果您需要自定义类型转换,可以提供一个 ConversionService Bean(Bean 名称 conversionService)或自定义属性编辑器(通过 CustomEditorConfigurer Bean)或自定义 Converters(带标注为 @ConfigurationPropertiesBinding 的 Bean 定义)。

由于该 Bean 在应用程序生命周期的非常早阶段被请求,因此请确保限制 ConversionService 使用的依赖项。通常,任何您需要的依赖项在创建时可能未完全初始化。如果您不需要用于配置密钥强制转换,并且仅依赖于使用 @ConfigurationPropertiesBinding 限定符的自定义转换器,则可以重命名您的自定义 ConversionService

Converting Durations

Spring Boot 专用于表达持续时间。如果您公开 java.time.Duration 属性,则应用程序属性中提供以下格式:

  • 常规 long 表示(使用毫秒作为默认单位,除非已指定 @DurationUnit

  • 标准的 ISO-8601 格式 {apiref-openjdk}/java.base/java/time/Duration.html#parse(java.lang.CharSequence)[java.time.Duration 使用的格式]

  • 一种更可读的格式,其中值和单位配对(`10s`表示 10 秒)。

请考虑以下示例:

要指定 30 秒的会话超时,30PT30S`和 `30s`都是等效的。以下任一形式都可以指定 500 毫秒的读取超时:`500PT0.5S`和 `500ms

您还可以使用任何受支持的单位。它们是:

  • ns for nanoseconds

  • us for microseconds

  • ms for milliseconds

  • s for seconds

  • m for minutes

  • h for hours

  • d for days

默认单位是毫秒,可以使用 `@DurationUnit`覆盖,如上例所示。

如果您更喜欢使用构造函数绑定,可以使用相同的属性,如下例所示:

如果您正在升级一个 Long`属性,请确保在它不是毫秒时定义单位(使用 `@DurationUnit)。这样做提供了透明的升级路径,同时支持更丰富的格式。

Converting Periods

除了持续时间之外,Spring Boot 还可以使用 `java.time.Period`类型。以下格式可用于应用程序属性:

  • 一个常规的 int`表示(使用天数作为默认单位,除非指定了 `@PeriodUnit

  • 标准的 ISO-8601 格式 {apiref-openjdk}/java.base/java/time/Period.html#parse(java.lang.CharSequence)[`java.time.Period`使用]

  • 一种更简单的格式,其中值和单位配对(`1y3d`表示 1 年和 3 天)

简单格式支持以下单位:

  • y for years

  • m for months

  • w for weeks

  • d for days

`java.time.Period`类型实际上从未存储周数,它是一个表示 "`7 days`"的快捷方式。

Converting Data Sizes

Spring Framework 有一个 `DataSize`值类型,表示以字节为单位的大小。如果您公开了一个 `DataSize`属性,应用程序属性中提供了以下格式:

  • 一个常规的 long`表示(使用字节作为默认单位,除非指定了 `@DataSizeUnit

  • 一种更可读的格式,其中值和单位配对(`10MB`表示 10 兆字节)

请考虑以下示例:

要指定 10 兆字节的缓冲区大小,10`和 `10MB`是等效的。可以将大小阈值指定为 256 字节,如下所示:`256`或 `256B

您还可以使用任何受支持的单位。它们是:

  • B for bytes

  • KB for kilobytes

  • MB for megabytes

  • GB for gigabytes

  • TB for terabytes

默认单位是字节,可以使用 `@DataSizeUnit`覆盖,如上例所示。

如果您更喜欢使用构造函数绑定,可以使用相同的属性,如下例所示:

升级 Long 属性时,请确保在非字节的情况下定义单位(使用 @DataSizeUnit)。这样做可以提供一条透明的升级路径,同时支持更丰富的格式。

@ConfigurationProperties Validation

Spring Boot 会尝试验证 @ConfigurationProperties 类,只要它们使用 Spring 的 @Validated 注解进行注释。您可以在配置类上直接使用 JSR-303 jakarta.validation 约束注解。为此,请确保您的类路径中存在兼容的 JSR-303 实现,然后将约束注解添加到字段中,如下例所示:

您还可以通过使用 @Validated`注释创建配置属性的 `@Bean 方法来触发验证。

为了确保即使未找到属性也始终触发嵌套属性的验证,关联的字段必须使用 @Valid`进行注释。以下示例基于前面的 `MyProperties 示例:

您还可以通过创建名为 configurationPropertiesValidator 的 Bean 定义来添加自定义 Spring Validator。应该将 @Bean 方法声明为 static。配置属性验证器在应用程序生命周期的早期创建,并将 @Bean 方法声明为静态允许在无需实例化 @Configuration 类的情况下创建 Bean。这样做避免了可能由早期实例化引起的任何问题。

spring-boot-actuator 模块包括一个端点,该端点公开所有 @ConfigurationProperties Bean。将您的 Web 浏览器指向 /actuator/configprops 或使用等效的 JMX 端点。“Production ready features” 部分提供了详细信息。

@ConfigurationProperties vs. @Value

@Value 注解是一个核心容器特性,它不提供与类型安全配置属性相同的功能。下表总结了 @ConfigurationProperties@Value 支持的功能:

Feature @ConfigurationProperties @Value

Relaxed binding

Yes

Limited (see note below)

Meta-data support

Yes

No

SpEL evaluation

No

Yes

如果您确实想要使用 @Value,我们建议您使用其规范形式引用属性名称(仅使用小写字母的连字符形式)。这将允许 Spring Boot 使用与使用 relaxed binding @ConfigurationProperties 相同的逻辑。 例如,@Value("${demo.item-price}") 将从 application.properties 文件中选取 demo.item-pricedemo.itemPrice 形式,以及从系统环境中选取 DEMO_ITEMPRICE。如果您改为使用 @Value("${demo.itemPrice}"),则不会考虑 demo.item-priceDEMO_ITEMPRICE

如果您为自己的组件定义了一组配置键,我们建议您将它们分组到使用 @ConfigurationProperties 进行注释的 POJO 中。这样做将为您提供结构化的类型安全对象,您可以将其注入到您自己的 Bean 中。

application property files 中的 SpEL 表达式在解析这些文件并填充环境时不会被处理。但是,可以在 @Value 中编写 SpEL 表达式。如果来自应用程序属性文件的属性值是 SpEL 表达式,则在通过 @Value 使用它时对其进行评估。