• 20220511 Core Features 2. Externalized Configuration


    前言

    文档地址

    Spring Boot 使您可以将配置外部化,以便可以在不同环境中使用相同的应用程序代码。您可以使用 Properties文件YAML文件环境变量命令行参数 来外部化配置。

    属性值可以通过 @Value 注解直接注入到你的 bean ,通过 Spring Environment 抽象 的访问,或者通过 @ConfigurationProperties 绑定到结构化对象

    Spring Boot 使用一个非常特殊的 PropertySource 顺序,该顺序旨在允许合理地覆盖值。属性按以下顺序考虑,优先级从低到高,数字更大的配置会覆盖更小的:

    1. 默认属性(通过设置 SpringApplication.setDefaultProperties 指定)
    2. @Configuration 类上的 @PropertySource 注解。请注意,在刷新应用程序上下文之前,不会将此类属性源添加到 Environment 中。配置某些属性(例如在刷新开始之前读取的 logging.*spring.main.* )为时已晚
    3. 配置数据(例如 application.properties 文件)
    4. 仅在 random.* 中具有属性的 RandomValuePropertySource
    5. 操作系统环境变量
    6. Java 系统属性, System.getProperties()
    7. 来自 java:comp/env 的 JNDI 属性
    8. ServletContext 初始化参数
    9. ServletConfig 初始化参数
    10. 来自 SPRING_APPLICATION_JSON(嵌入在环境变量或系统属性中的内联 JSON )的属性
    11. 命令行参数
    12. tests 中的 properties 属性。可用于 @SpringBootTest 注解和测试应用程序的特定部分的测试注解
    13. tests 中的 @TestPropertySource 注解
    14. devtools 处于活动状态时,文件夹 $HOME/.config/spring-boot 中的 Devtools 全局设置属性

    配置数据文件按以下顺序考虑,优先级从低到高,数字更大的配置会覆盖更小的:

    1. 打包 jar 中的 应用程序属性application.properties 和 YAML 变体)
    2. 打包 jar 中的 特定于 profile 的应用程序属性application-{profile}.properties 和 YAML 变体)
    3. 打包 jar 外的 应用程序属性application.properties 和 YAML 变体)
    4. 打包 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"

    envconfigprops 端点可用于确定属性为何具有特定值。您可以使用这两个端点来诊断意外的属性值。有关详细信息,请参阅 生产就绪功能 部分。

    2.1. 访问命令行属性

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

    如果您不想将命令行属性添加到 Environment 中,可以使用 SpringApplication.setAddCommandLineProperties(false) 禁用它们

    2.2. JSON 应用属性

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

    当您的应用启动时,任何 spring.application.jsonSPRING_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 值将被添加到生成的属性源中,但 PropertySourcesPropertyResolvernull 属性视为缺失值。这意味着 JSON 不能用 null 值覆盖来自低阶属性源的属性

    2.3. 外部应用程序属性

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

    1. 从类路径
      1. 类路径根
      2. 类路径 /config
    2. 从当前目录
      1. 当前目录
      2. 当前目录中的 /config 子目录
      3. /config 子目录的直接子目录

    该列表按优先级由低到高排序(高优先级覆盖低优先级)。加载文件中的文档被作为 PropertySources 添加到 Spring Environment 中。

    如果您不喜欢 application 作为配置文件名,您可以通过指定 spring.config.name 环境属性来切换到另一个文件名。例如,要查找 myproject.propertiesmyproject.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/ ,则完整位置集是:

    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/

    这种搜索顺序允许您在一个配置文件中指定默认值,然后在另一个配置文件中选择性地覆盖这些值。您可以在默认位置之一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.locationspring.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.locationspring.config.additional-location 属性一起使用。

    对于作为目录的搜索位置或 */<filename> 作为文件的搜索位置, 通配符位置必须仅包含一个 * 并以 */ 结尾。带有通配符的位置根据文件名的绝对路径按字母顺序排序。

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

    2.3.3. 特定 profile 配置文件

    除了 application 属性文件,Spring Boot 还将尝试使用命名约定 application-{profile} 加载特定于 profile 的文件。例如,如果您的应用程序激活了一个名为 prod 的 profile 并使用 YAML 文件,那么 application.ymlapplication-prod.yml 两者都会被考虑。

    特定于 profile 的属性从与标准 application.properties 相同的位置加载,特定于 profile 的文件总是覆盖非特定文件。如果指定了多个 profile ,则应用 最后获胜( last-wins )策略 。例如,如果 spring.profiles.active 属性指定 profile 为 prod,live 由,则 application-prod.properties 中的值可以被 application-live.properties 中的值覆盖。

    最后获胜的策略适用于 位置组 级别。spring.config.locationclasspath:/cfg/,classpath:/ext/ 不会具有与 classpath:/cfg/;classpath:/ext/ 相同的覆盖规则。

    例如,继续上面 prod,live 的示例,我们可能有以下文件:

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

    当我们设置 spring.config.locationclasspath:/cfg/,classpath:/ext/ 时,我们在所有 /ext 文件之前处理所有 /cfg 文件

    1. /cfg/application-live.properties
    2. /ext/application-prod.properties
    3. /ext/application-live.properties

    当我们设置 spring.config.locationclasspath:/cfg/;classpath:/ext/ 时,我们在同一级别处理所有 /ext/cfg 文件

    1. /ext/application-prod.properties
    2. /cfg/application-live.properties
    3. /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 包中的 ConfigDataLocationResolverConfigDataLoader 类。

    2.3.5. 导入无扩展名文件

    某些云平台无法为卷挂载文件添加文件扩展名。要导入这些无扩展名文件,您需要给 Spring Boot 一个提示,以便它知道如何加载它们。您可以通过将扩展提示放在方括号中来做到这一点。

    例如,假设您有一个 /etc/config/myconfig 文件要导入为 yaml 。您可以使用以下命令从您的 application.properties 导入它:

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

    2.3.6. 使用配置树

    在云平台(例如 Kubernetes)上运行应用程序时,您通常需要读取平台提供的配置值。将环境变量用于此类目的并不少见,但这可能有缺点,尤其是在值应该保密的情况下。

    作为环境变量的替代方案,许多云平台现在允许您将配置映射到挂载的数据卷。例如,Kubernetes 可以同时卷挂载 ConfigMapsSecrets

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

    1. 单个文件包含一组完整的属性(通常写为 YAML)
    2. 多个文件被写入目录树,文件名成为“键”,内容成为“值”

    对于第一种情况,您可以使用 上述 spring.config.import 方法直接导入 YAML 或属性文件。对于第二种情况,您需要使用 configtree: 前缀,以便 Spring Boot 知道它需要将所有文件公开为属性。

    例如,假设 Kubernetes 挂载了以下卷:

    etc/
      config/
        myapp/
          username
          password
    

    username 文件的内容将是一个配置值,password 的内容将是一个 secret

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

    spring.config.import=optional:configtree:/etc/config/
    

    然后,您可以以通常的方式从 Environment 访问或注入 myapp.usernamemyapp.password 属性。

    配置树下的文件夹构成属性名称。在上面的示例中,要访问 usernamepassword 的属性,您可以设置 spring.config.importoptional:configtree:/etc/config/myapp

    带有点符号的文件名也被正确映射。例如,在上面的示例中,/etc/config 下名为 myapp.username 的文件将导致 Environment 中的属性 myapp.username

    根据预期的内容 , 配置树值可以绑定到字符串 Stringbyte[] 类型

    如果您要从同一个父文件夹导入多个配置树,则可以使用通配符快捷方式。任何以 /*/ 结尾的 configtree: 位置都会将所有直接子项导入为配置树。

    例如,给定以下卷:

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

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

    spring.config.import=optional:configtree:/etc/config/*/
    

    这将添加 db.usernamedb.passwordmq.usernamemq.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.propertiesapplication.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 上运行时才处于活动状态,并且仅当 prodstaging 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 类绑定到 Java ListSet 。有关更多详细信息,请参阅 类型安全配置属性

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

    直接加载 YAML

    Spring Framework 提供了两个方便的类,可用于加载 YAML 文档。YamlPropertiesFactoryBean 加载 YAML 为 PropertiesYamlMapFactoryBean 加载 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) CLOSEOPEN,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 管理和验证应用程序的配置。

    另请参见 @Value 和类型安全配置属性之间的区别

    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.rolesString 集合,默认为 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 出现警告信息:
    image

    解决方法:加入依赖

    <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 实例将包含 securitynull 值。如果您希望即使没有绑定任何属性也返回一个非空 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/key2key3 作为映射中的键。斜线已被删除,因为 key3 没有被方括号包围。

    当绑定到标量值时,带有 . 的键不需要用 [] 。标量值包括枚举和 java.lang 包中除 Object 的所有类型。绑定 a.b=cMap<String, String> 将保留键中的 . 并返回带有 {"a.b"="c"} 条目的 Map

    对于任何其他类型,如果您的 key 包含 . 需要用中括号包围。例如,绑定 a.b=cMap<String, Object> 将返回带有条目 {"a"={"b"="c"}}Map,而 [a.b]=c 将返回带有条目 {"a.b"="c"}Map

    从环境变量绑定

    大多数操作系统对可用于环境变量的名称施加了严格的规则。例如,Linux shell 变量只能包含字母( a - zA - 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 对象具有默认值为 nullnamedescription 属性。以下示例公开了来自 MyPropertiesMyPojo 对象列表:

    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 仍然只包含一个条目( namemy another namedescriptionnull )。此配置不会将第二个 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 条目( namemy another namedescriptionnull )。对于 YAML ,逗号分隔的列表和 YAML 列表都可用于完全覆盖列表的内容。

    对于 Map 属性,您可以绑定从多个来源提取的属性值。但是,对于多个源中的相同属性,将使用具有最高优先级的属性。以下示例公开了来自 MyPropertiesMap<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 的条目( namemy name 1descriptionmy description 1 )。但是,如果启用了 dev profile ,则 map 包含两个条目,键 key1namedev name 1descriptionmy description 1 )和 键 key2namedev name 2descriptiondev 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 秒的 sessionTimeout30PT30S30s 都等效。500毫秒的 readTimeout 可以以任何形式如下指定:500PT0.5S500ms

    也可以使用任何受支持的单位:

    • 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 的 bufferSize1010MB 等效。可以将 256 个字节的 sizeThreshold 指定为 256256B

    也可以使用任何受支持的单位:

    • 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-pricedemo.itemPrice ,以及系统环境的 DEMO_ITEMPRICE。如果你改用了 @Value("{demo.itemPrice}") ,就不会支持 demo.item-priceDEMO_ITEMPRICE

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

    在解析这些文件和填充环境时,不会处理来自 应用属性文件SpEL 表达式。但是,可以在 @Value 中编写 SpEL 表达式。如果应用属性文件中的属性值是一个 SpEL 表达式,则在通过 @Value 使用时将对其进行计算。

  • 相关阅读:
    ORACLE定时备份
    解压cpio.gz
    Fusioncharts的数字格式化
    linux apache+php+mysql安装及乱码解决办法
    两个Beta函数类型的积分及其一般形式
    【转载】巴塞尔问题(Basel Problem)的多种解法
    两本关于各种刁钻积分计算的书
    一个超几何函数类型的积分
    一个取整函数积分的一般形式
    Clausen Functions (and related series, functions, integrals)
  • 原文地址:https://www.cnblogs.com/huangwenjie/p/16354158.html
Copyright © 2020-2023  润新知