Spring Boot 参考 核心功能 外部化配置 外部化配置 Spring Boot 允许你将配置外置,以便你可以在不同的环境中使用相同的应用程序代码。你可以使用各种外部配置源,包括 Java 属性文件、YAML 文件、环境变量和命令行参数。 可以通过使用 @Value 注解将属性值直接注入到你的 Bean 中,通过 Spring 的 Environment 抽象访问,或通过 @ConfigurationProperties 绑定到结构化对象。 Spring Boot 使用非常特定的 PropertySource 顺序,该顺序旨在允许合理地覆盖值。后面的属性源可以覆盖前面定义的值。源按以下顺序考虑 默认属性(通过设置 SpringApplication.setDefaultProperties 指定)。 @PropertySource 在你的 @Configuration 类上的注解。请注意,此类属性源在应用程序上下文刷新之前不会添加到 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 中。 例如,SPRING_APPLICATION_JSON 属性可以在命令行中作为环境变量在 UN*X shell 中提供 $ 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 生成的名称。 目录和文件位置值也会展开以检查 特定于配置文件的文件。例如,如果您有 classpath:myconfig.properties 的 spring.config.location,您还将发现加载了适当的 classpath:myconfig-<profile>.properties 文件。 在大多数情况下,您添加的每个 spring.config.location 项将引用一个文件或目录。位置按定义的顺序进行处理,后面的位置可以覆盖前面的位置的值。 如果您有复杂的 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/ 可选:文件:./;可选:文件:./config/;可选:文件:./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 中的值覆盖。 最后获胜策略应用于 位置组 级别。classpath:/cfg/,classpath:/ext/ 的 spring.config.location 不会与 classpath:/cfg/;classpath:/ext/ 具有相同的覆盖规则。 例如,继续我们上面 prod,live 的示例,我们可能有以下文件 /cfg application-live.properties /ext application-live.properties application-prod.properties 当我们有 classpath:/cfg/,classpath:/ext/ 的 spring.config.location 时,我们在所有 /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])。换句话说,如果未明确激活任何配置文件,则考虑使用 application-default 中的属性。 属性文件只加载一次。如果您已经直接 导入 了特定于配置文件的属性文件,则不会再次导入。 导入其他数据 应用程序属性可以使用 spring.config.import 属性从其他位置导入其他配置数据。导入在发现时进行处理,并作为立即插入到声明导入的文档正下方的其他文档进行处理。 例如,您可能在 classpath 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。 如果您想支持自己的位置,请参阅 org.springframework.boot.context.config 包中的 ConfigDataLocationResolver 和 ConfigDataLoader 类。 导入无扩展名文件 有些云平台无法为卷装载文件添加文件扩展名。要导入这些无扩展名文件,您需要向 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 或 Properties 文件,如 上文所述。对于第二种情况,您需要使用 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 群集服务被授予访问密钥的权限时,该密钥将被装载到容器中。例如,如果名为 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。 在占位符中引用属性名称时,应始终使用其规范形式(仅使用小写字母的连字符形式)。这将允许 Spring Boot 使用与 relaxed binding @ConfigurationProperties 相同的逻辑。 例如,${demo.item-price} 将从 application.properties 文件中选取 demo.item-price 和 demo.itemPrice 形式,以及从系统环境中选取 DEMO_ITEMPRICE。如果您改用 ${demo.itemPrice},则 demo.item-price 和 DEMO_ITEMPRICE 将不被考虑。 您还可以使用此技术创建现有 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.* 有条件地激活属性文档。 以下激活属性可用 表 1. 激活属性 属性 说明 on-profile 文档要处于活动状态,必须匹配的配置文件表达式。 on-cloud-platform 文档要处于活动状态,必须检测到的 CloudPlatform。 例如,以下内容指定仅在 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。有关详细信息,请参阅 在启动之前自定义 Environment 或 ApplicationContext。 如果你需要一种安全的方式来存储凭据和密码,Spring Cloud Vault 项目提供了在 HashiCorp Vault 中存储外部化配置的支持。 使用 YAML YAML 是 JSON 的超集,因此,它是一种方便的格式,用于指定分层配置数据。只要你的类路径上有 SnakeYAML 库,SpringApplication 类就会自动支持 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 对象。有关更多详细信息,请参阅下面的“类型安全配置属性”部分。 YAML 文件无法使用 @PropertySource 或 @TestPropertySource 注解加载。因此,如果你需要以这种方式加载值,则需要使用属性文件。 直接加载 YAML Spring Framework 提供了两个便捷的类,可用于加载 YAML 文档。YamlPropertiesFactoryBean 将 YAML 加载为 Properties,YamlMapFactoryBean 将 YAML 加载为 Map。 如果您想将 YAML 加载为 Spring PropertySource,也可以使用 YamlPropertySourceLoader 类。 配置随机值 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 管理和验证应用程序的配置。 另请参阅 @Value 和类型安全配置属性之间的差异。 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 只要地图已初始化,就需要 getter 但不一定需要 setter,因为它们可以被绑定器修改。 可以通过索引(通常使用 YAML)或使用单个逗号分隔值(属性)来访问集合和数组。在后一种情况下,setter 是强制性的。我们建议始终为这些类型添加 setter。如果初始化集合,请确保它不是不可变的(如前面的示例中所示)。 如果嵌套的 POJO 属性已初始化(如前面的示例中的 Security 字段),则不需要 setter。如果您希望绑定器使用其默认构造函数动态创建实例,则需要一个 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>) } 在此设置中,单个参数化构造函数的存在意味着应使用构造函数绑定。这意味着绑定器将找到具有您希望绑定的参数的构造函数。如果您的类有多个构造函数,则可以使用 @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。 启用 @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 当使用配置属性扫描或通过 @EnableConfigurationProperties 注册 @ConfigurationProperties Bean 时,该 Bean 有一个传统名称:<prefix>-<fqn>,其中 <prefix> 是 @ConfigurationProperties 注释中指定的 environment key 前缀,<fqn> 是 Bean 的完全限定名称。如果注释没有提供任何前缀,则只使用 Bean 的完全限定名称。 假设它在 com.example.app 包中,上面 SomeProperties 示例的 Bean 名称是 some.properties-com.example.app.SomeProperties。 我们建议 @ConfigurationProperties 仅处理环境,特别是不要注入来自上下文的其他 Bean。对于特殊情况,可以使用 setter 注入或框架提供的任何 *Aware 接口(例如,如果你需要访问 Environment,可以使用 EnvironmentAware)。如果你仍想使用构造函数注入其他 Bean,则必须使用 @Component 对配置属性 Bean 进行注释,并使用基于 JavaBean 的属性绑定。 使用带 @ConfigurationProperties 注释的类型 这种配置样式特别适用于 SpringApplication 外部 YAML 配置,如下面的示例所示 my: service: remote-address: 192.168.1.1 security: username: "admin" roles: - "USER" - "ADMIN" 要使用 @ConfigurationProperties Bean,你可以像注入任何其他 Bean 一样注入它们,如下面的示例所示 Java Kotlin import org.springframework.stereotype.Service; @Service public class MyService { private final MyProperties properties; public MyService(MyProperties properties) { this.properties = properties; } public void openConnection() { Server server = new Server(this.properties.getRemoteAddress()); server.start(); // ... } // ... } import org.springframework.stereotype.Service @Service class MyService(val properties: MyProperties) { fun openConnection() { val server = Server(properties.remoteAddress) server.start() // ... } // ... } 使用 @ConfigurationProperties 还允许你生成元数据文件,IDE 可以使用这些文件为你的键提供自动完成。有关详细信息,请参阅附录。 第三方配置 除了使用 @ConfigurationProperties 对类进行注释外,你还可以对公共 @Bean 方法使用它。当你想要将属性绑定到不受你控制的第三方组件时,这样做会特别有用。 要从 Environment 属性配置 Bean,请将其 @ConfigurationProperties 添加到其 Bean 注册中,如下面的示例所示 Java Kotlin import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration(proxyBeanMethods = false) public class ThirdPartyConfiguration { @Bean @ConfigurationProperties(prefix = "another") public AnotherComponent anotherComponent() { return new AnotherComponent(); } } import org.springframework.boot.context.properties.ConfigurationProperties import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration @Configuration(proxyBeanMethods = false) class ThirdPartyConfiguration { @Bean @ConfigurationProperties(prefix = "another") fun anotherComponent(): AnotherComponent = AnotherComponent() } 使用 another 前缀定义的任何 JavaBean 属性都以类似于前面 SomeProperties 示例的方式映射到该 AnotherComponent Bean。 宽松绑定 Spring Boot 使用一些宽松规则将 Environment 属性绑定到 @ConfigurationProperties Bean,因此 Environment 属性名称和 Bean 属性名称之间不需要完全匹配。此功能有用的常见示例包括用破折号分隔的环境属性(例如,context-path 绑定到 contextPath)和大写环境属性(例如,PORT 绑定到 port)。 例如,考虑以下 @ConfigurationProperties 类 Java Kotlin import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = "my.main-project.person") public class MyPersonProperties { private String firstName; public String getFirstName() { return this.firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } } import org.springframework.boot.context.properties.ConfigurationProperties @ConfigurationProperties(prefix = "my.main-project.person") class MyPersonProperties { var firstName: String? = null } 使用前面的代码,可以使用以下属性名称 表 2. 宽松绑定 属性 说明 my.main-project.person.first-name 中划线形式,建议在 .properties 和 YAML 文件中使用。 my.main-project.person.firstName 标准驼峰式语法。 my.main-project.person.first_name 下划线表示法,是 .properties 和 YAML 文件中的备用格式。 MY_MAINPROJECT_PERSON_FIRSTNAME 大写格式,建议在使用系统环境变量时使用。 注释的 prefix 值必须采用中划线形式(小写并用 - 分隔,例如 my.main-project.person)。 表 3. 每个属性源的宽松绑定规则 属性源 简单 列表 属性文件 驼峰式、中划线形式或下划线表示法 使用 [ ] 或逗号分隔值的标准列表语法 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"} 条目的映射。对于任何其他类型,如果 key 包含 .,则需要使用方括号表示法。例如,将 a.b=c 绑定到 Map<String, Object> 会返回一个包含 {"a"={"b"="c"}} 条目的映射,而 [a.b]=c 会返回一个包含 {"a.b"="c"} 条目的映射。 从环境变量绑定 大多数操作系统对环境变量可用的名称施加了严格的规则。例如,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 的环境变量。 缓存 松散绑定使用缓存来提高性能。默认情况下,此缓存仅应用于不可变属性源。要自定义此行为,例如启用可变属性源的缓存,请使用 ConfigurationPropertyCaching。 合并复杂类型 如果在多个位置配置列表,则覆盖操作将通过替换整个列表来起作用。 例如,假设一个 MyPojo 对象具有 name 和 description 属性,默认情况下为 null。以下示例从 MyProperties 公开了一个 MyPojo 对象列表 Java Kotlin import java.util.ArrayList; import java.util.List; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties("my") public class MyProperties { private final List<MyPojo> list = new ArrayList<>(); public List<MyPojo> getList() { return this.list; } } import org.springframework.boot.context.properties.ConfigurationProperties @ConfigurationProperties("my") class MyProperties { val list: List<MyPojo> = ArrayList() } 考虑以下配置 属性 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(bean 定义标注为 @ConfigurationPropertiesBinding)。 由于此 bean 在应用程序生命周期中很早就被请求,因此请务必限制 ConversionService 使用的依赖项。通常,您需要的任何依赖项在创建时可能尚未完全初始化。如果您不需要配置键强制转换,并且仅依赖于标注为 @ConfigurationPropertiesBinding 的自定义转换器,则可能需要重命名自定义 ConversionService。 转换持续时间 Spring Boot 专用于表示持续时间。如果您公开一个 java.time.Duration 属性,则应用程序属性中可以使用以下格式 常规 long 表示(使用毫秒作为默认单位,除非指定了 @DurationUnit) java.time.Duration 使用的标准 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) java.time.Period 使用的标准 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 Boot 会尝试验证 @ConfigurationProperties 类,只要它们使用 Spring 的 @Validated 注解进行注释。您可以在配置类上直接使用 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 宽松绑定 是 有限(请参阅以下注释) 元数据支持 是 否 SpEL 评估 否 是 如果您确实想使用 @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。 如果您为自己的组件定义了一组配置键,我们建议您将它们分组到使用 @ConfigurationProperties 注释的 POJO 中。这样做将为您提供结构化、类型安全的对象,您可以将其注入到您自己的 bean 中。 来自 应用程序属性文件 的 SpEL 表达式在解析这些文件和填充环境时不会被处理。但是,可以在 @Value 中编写 SpEL 表达式。如果应用程序属性文件中的属性值是 SpEL 表达式,那么在通过 @Value 使用时将对其进行评估。 SpringApplication 配置文件