• 一文学会Spring Boot配置属性


    一文学会Spring Boot配置属性

    Spring Boot允许将配置外部化,以便在不同的环境中使用相同的程序代码。可以使用各种外部配置源,包括Java属性文件、YAML文件、环境变量、命令行参数。配置属性值可以通过@Value注解直接注入到bean中,或者通过@ConfigurationProperties注解绑定到结构化对象。

    Spring Boot中的配置具有顺序性,该顺序允许程序员合理的覆盖属性值,顺序高的配置值可以覆盖顺序低的配置值,优先级从低到高如下

    1. 默认属性 - 可通过 SpringApplication.setDefaultProperties 设置
    2. @Configuration 类上配置的 @PropertySource 注解 - 需要注意在刷新应用上下文context之前,不会将此类属性添加到 Environment 中。对于特殊的场景配置会导致读不到属性值,如 logging.* and spring.main.*
    3. Config Data - 例如 application.properties 文件
    4. 配置随机属性值
    5. 操作系统环境变量
    6. JVM 环境变量 - 如可以通过System.getProperties()获取的属性值
    7. 从 java:comp/env 获取的 JNDI 属性值
    8. ServletConext 初始化参数
    9. ServletConfig 初始化参数
    10. SPRING_APPLICATION_JSON 中的属性值
    11. 命令行启动参数
    12. 测试属性值 - 使用 @SpringBootTest 、测试注解中的属性值
    13. @TestPropertySource 注解
    14. devtools 启用时,Devtools 工具在 $HOME/.config/spring-boot 目录设置的全局属性

    配置文件的优先级顺序如下:

    • jar包内部的 application.properties 配置文件
    • profile 属性指定jar包内部的配置文件 - 如application-{profile}.properties
    • jar包外部的 application.properties 配置文件
    • profile 属性指定jar包外部的配置文件 - 如application-{profile}.properties

    建议在项目中使用相同格式的配置文件,统一为 .properties 或 .yml 格式。假设在相同的目录中同时存在两种类型的配置文件,.properties优先

    @Value

    @Component
    public class MyBean {
    
        @Value("${name}")
        private String name;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    以上代码使用 @Value注解读取配置文件的name值。可以应用不同的场景:

    • jar包内部 application.properties 中配置的name属性

    • 运行新环境时,jar外部 application.properties 文件中name属性值可以覆盖jar包内部的属性值

    • 测试时,可以通过命令行覆盖属性值

      java -jar app.jar --name="Spring"
      
      • 1

    获取命令行属性值

    默认情况下,Spring Boot 可以转化命令中 以 --开头的参数,如 --server.port=9000,并把它们添加到Spring Environment环境中,命令行的参数始终偶先配置文件中的参数。

    如果不想将命令行中的属性配置添加到 Environment中,可以通过代码禁用该行为:

    SpringApplication.setAddCommandLineProperties(false)
    
    • 1

    JSON 应用属性

    环境变量跟系统属性可能会存在一些属性名称上的冲突,为了解决此类问题。Spring Boot允许将属性代码快编码为单个的JSON结构。启动应用程序时,spring.application.json (SPRING_APPLICATION_JSON) 属性值将被解析并添加到 Environment环境中。

    例如,SPRING_APPLICATION_JSON

    // 方式一
    java -jar target/springboot-study-0.0.1-SNAPSHOT.jar --spring.application.json='{\"name\":\"test\"}'
    
    // 方式二
    java -jar target/springboot-study-0.0.1-SNAPSHOT.jar -Dspring.application.json='{\"name\":\"test\"}'
    
    • 1
    • 2
    • 3
    • 4
    • 5

    外部属性文件

    系统启动时,Spring Boot 自动从指定的目录加载 application.propertiesapplication.yaml 配置文件。指定目录(优先级顺序从低到高)如下:

    1. 从 classpath 加载
      • classpath 根目录
      • classpath 下 /config 目录
    2. 从当前目录 加载
      • 当前目录加载
      • 当前目录下的 config 子目录加载
      • config 下的子目录加载

    如果不喜欢默认的配置文件名 application,可以通过 spring.config.name 在命令行中指定其他配置文件名称,如下指定 myproject.properties 或 myproject.yaml 文件:

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

    也可以显示指定系统加载的文件,多个目录之间使用逗号进行分隔

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

    大多数情况下,每个spring.config选项关联单个文件或者目录。多个目录之间按照定义顺序进行处理,后面的位置可以覆盖前面位置的值。spring.config.location配置的目录将会替换默认的读取路径。例如 spring.config.location 配置为 “optional:classpath:/custom-config/,optional:file:./custom-config/”,Spring Boot 读取的完整路径为:

    • optional:classpath:custom-config/
    • optional:file:./custom-config/

    如果只是新增路径,然不是替换默认路径。可以使用 spring.config.additional-location 配置。从其他位置加载的属性配置将覆盖默认的属性配置。例如 spring.config.additional-location 配置为 optional:classpath:/custom-config/,optional:file:./custom-config/, Spring Boot 将会读取的路径为:

    • optional:classpath:/;optional:classpath:/config/
    • optional:file:./;optional:file:./config/;optional:file:./config/*/
    • optional:classpath:custom-config/`
    • optional:file:./custom-config/

    Spring Boot允许你有选择性的覆盖另一个配置中的值。开发人员可以在应用程序中为应用程序提供默认值。然后在运行时指定一个特定的路径位置然后覆盖这些默认值。

    Optional Locations

    默认情况下,当指定的配置文件路径不存在时,Spring Boot 将会终止启动应用程序,并抛出异常 ConfigDataLocationNotFoundException。

    如果你想指定路径,但并不确定该路径是否一直存在,你可以加上 optional: 前缀。spring.config.location 、spring.config.additional-location都可以使用 这个前缀,也可以使用 spring.config.import 声明。例如, spring.config.import 值为 optional:file:./myconfig.properties 允许应用程序启动,及时 myconfig.properties文件不存在。

    如果想忽略ConfigDataLocationNotFoundException异常,并继续启动应用,可以使用 spring.config.on-not-found,值设置为 ingore。系统使用SpringApplication.setDefaultProperties(…) 或 system/environment 中的值

    Wildcard Locations

    如果配置路径中包含 * 号,则它将被视为通配符位置。加载配置文件时会将子目录也考虑进去。例如 需要配置Redis、Mysql配置文件,考虑将这两部分配置文件分开,方便管理。这将导致两个文件

    • /config/redis/application.properties
    • /config/mysql/application.properties

    这种情况需要使用通配符进行处理,让Spring Boot识别并加载这两个目录下的文件,如 config/*/。默认情况下,Spring Boot 在默认的搜索位置搜索 config/*/ 目录,jar包外部的 /config 文件夹也会被识别加载。

    通配符位置仅适用于外部目录,不能在classpath: 下 使用通配符

    Profile Specific Files

    Spring Boot 允许使用 application-{profile} 加载不同环境的配置文件。例如,应用程序在 prod 生产环境中启动,则配置文件命名为 application-prod.yml。跟标准的配置文件加载流程类似,特定环境下profile配置文件会覆盖默认的配置文件。

    如果多个 profile 文件被指定,默认应用后面的profile文件 (last-wins strategy),如 同时在 prod、live profile配置文件中指定spring.profiles.active属性,application-live.properties 配置文件中的值 覆盖 application-prod.properties.

    last-wins strategy应用于目录组级别,即相同的目录组,后面文件的配置属性覆盖前面文件的配置属性。还是使用上面的案例,同时指定 prod、live 配置文件,如下:
    /cfg
      application-live.properties
    /ext
      application-live.properties
      application-prod.properties
    使用spring.config.location属性,配置值为: classpath:/cfg/,classpath:/ext/,则文件处理顺序如下:
    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    系统使用默认的profile配置文件,默认值为 [default],即没有显示指定profile配置文件时,则使用 application-default 配置文件。

    Importing Additional Data

    应用程序可以使用spring.config.import属性从其他目录导入配置文件,导入的文件被当成原配置文件的一部分进行处理,如下:

    spring:
      application:
        name: "myapp"
      config:
        import: "optional:file:./dev.properties"
    
    • 1
    • 2
    • 3
    • 4
    • 5

    上述配置将导入当前目录下的dev.properties(如果文件存在)。dev.properties属性文件的值将优先处理,在dev.properties中可以重新定义 spring.application.name的值。

    在properties/yaml 单个文件中导入的顺序并不重要,如下两个配置它们的结果是一样的。

    spring:
      config:
        import: "my.properties"
    my:
      property: "value"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    my:
      property: "value"
    spring:
      config:
        import: "my.properties"
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在上述的两个示例中,mysql.properties中的值将优先于文件中定义的属性值。使用 spring.config.import 导入多个文件时,目录文件按顺序处理,后面导入的文件优先级高。

    Importing Extensionless Files

    一些云平台需要导入外部文件系统的配置文件,应对这种场景,需要告诉Spring Boot如何加载他们。

    假如需要在application.properties 导入 /etc/config/myconfig文件,使用示例如下

    spring:
      config:
        import: "file:/etc/config/myconfig[.yaml]"
    
    • 1
    • 2
    • 3

    Using Configuration Trees

    etc/
      config/
        myapp/
          username
          password
    
    • 1
    • 2
    • 3
    • 4
    • 5

    假设存在如上目录配置文件,需要同时读取他们的配置属性。当然可以使用 spring.config.import 一个个的导入,但是可以使用以下更直观的方式导入:

    spring:
      config:
        import: "optional:configtree:/etc/config/"
    
    • 1
    • 2
    • 3

    可以在Environment 环境变量中使用 myapp.username 、myapp.password 配置属性。

    如果同一个父目录,存在多个子配置目录,可以使用通配符,减少代码代码量,如下:

    etc/
      config/
        dbconfig/
          db/
            username
            password
        mqconfig/
          mq/
            username
            password
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    spring:
      config:
        import: "optional:configtree:/etc/config/*/"
    
    • 1
    • 2
    • 3

    Property Placeholders

    application.properties 、applicaiton.yml中的属性值在使用时会经过 Environment处理,因此配置可以关联之前已经定义的值(从系统属性、环境变量中关联)。${name}占位符可以使用在任何位置。属性占位符还可以指定默认值,如 ${name:default}.

    app:
      name: "MyApp"
      description: "${app.name} is a Spring Boot application written by ${username:Unknown}"
    
    • 1
    • 2
    • 3

    username 可能其他地方没有设置,配置默认值;app.name为设置为 MyApp,因此description的最终结果为:MyApp is a Spring Boot application written by Unknown.

    Working With Multi-Document Files

    Spring Boot 允许将单个文件拆分程多个文件,每个文件单独添加,文档按从上到下的顺序处理,后面的文件可以覆盖前面文件中定义的属性。YAML支持多文档语法,— 字符串表示一个文档的结尾,下一个文档的开始。

    以下配置代表两个逻辑文件

    spring:
      application:
        name: "MyApp"
    ---
    spring:
      application:
        name: "MyCloudApp"
      config:
        activate:
          on-cloud-platform: "kubernetes"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    对于properties属性文件 使用 #— 、!— 字符串分隔文件

    spring.application.name=MyApp
    #---
    spring.application.name=MyCloudApp
    spring.config.activate.on-cloud-platform=kubernetes
    
    • 1
    • 2
    • 3
    • 4

    Activation Properties

    在一些场景中,当条件满足时,需要激活相关的属性配置。可以使用 spring.config.activate.*. 有条件的技术配置属性。

    PropertyNote
    on-profile表达式与配置文件匹配,才能激活相关属性
    on-cloud-platform当配置处于活动状态,CloudPlatform配置值才被有效
    myprop:
      "always-set"
    ---
    spring:
      config:
        activate:
          on-cloud-platform: "kubernetes"
          on-profile: "prod | staging"
    myotherprop: "sometimes-set"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    Working With YAML

    YAML是一个分层的、格式良好的数据配置文件,跟JSON数据一样,也是Key-Value数据格式存储。Spring Boot 内部使用SnakeYAML 第三方 jar包 支持 YAML格式文件的解析。

    如果使用Starter包,SnakeYAML jar包已经被包含在 spring-boot-starter.

    Mapping YAML to Properties

    YAML 文档根据其分层结构转换为 Key-Value键值对存储在Spring Environment 环境中,例如:

    environments:
      dev:
        url: "https://dev.example.com"
        name: "Developer Setup"
      prod:
        url: "https://another.example.com"
        name: "My Cool App"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    Spring 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
    
    • 1
    • 2
    • 3
    • 4

    同时,YAML支持组数配置,使用 [index] 解析器表示数组下标,如:

    my:
     servers:
     - "dev.example.com"
     - "another.example.com"
    
    • 1
    • 2
    • 3
    • 4

    上述example将被转换为:

    my.servers[0]=dev.example.com
    my.servers[1]=another.example.com
    
    • 1
    • 2

    Directly Loading YAML

    Spring Framework 提供了两个类来加载YAML配置文件

    • YamlPropertiesFactoryBean - 将YAML作为properties属性加载
    • YamlMapFactoryBean - 将YAML作为Map加载

    配置随机属性

    RandomValuePropertySource 类用于注入随机配置属性(例如,配置秘钥、测试用例)。可以生成Integer、Long、uuids、strings,配置如下:

    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]}"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    Type-safe Configuration Properties

    使用@Value(“${property}”)注解注入属性文件时,有些时候可能存在问题。尤其是使用多个属性、或者数据分层时,Spring Boot 提供了另一种处理属性的方法,它允许强类型bean管理、验证应用程序的配置。

    JavaBean 属性绑定

    绑定Bean中定义的属性值(Get、Set方法),使用如下:

    @ConfigurationProperties("my.service")
    public class MyProperties {
    
        private boolean enabled;
    
        private InetAddress remoteAddress;
    
        private final Security security = new Security();
    
        // getters / setters...
    
        public static class Security {
    
            private String username;
    
            private String password;
    
            private List roles = new ArrayList<>(Collections.singleton("USER"));
    
            // getters / setters...
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    Bean 与 配置文件之间的对应关系为:

    • my.service.enabled 对应 enabled属性,默认值为 false
    • my.service.remote-address 对应remoteAddress属性,配置为字符串
    • my.service.security.username 对应内部类Security中的name属性
    • my.service.security.password 对应内部类Security中的password属性
    • my.service.security.roles 对应内部类roles属性,配置为String 数组

    构造函数绑定

    Spring Boot 支持以构造函数方式注入配置属性值,代码如下:

    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;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

    @ConstructorBinding注解用于指定使用构造函数绑定配置属性值,内部类的构造函数也可以使用该注解绑定属性值。

    通过构造函数绑定属性值时,可以使用@DefaultValue注解设置属性默认值。

    引用之前的带啊,如果文件中没有配置 Security值,MyProperties 实例中 security字段设置为null。为了避免null值,可以通过 @DefaultValue 指定默认值,如下:

    public MyProperties(boolean enabled, InetAddress remoteAddress, @DefaultValue Security security) {
        this.enabled = enabled;
        this.remoteAddress = remoteAddress;
        this.security = security;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    使用构造函数绑定,Spring Boot应用程序必须开启 @EnableConfigurationProperties 属性、或者配置属性扫描。不能使用构造函数绑定来创建 @Component 声明的bean

    Enabling @ConfigurationProperties-annotated Types

    Spring Boot 提供 @ConfigurationProperties 声明一个class文件,并将它注册为bean。可以单个启动配置属性,也可以通过扫描启动多个配置属性。

    使用@ConfigurationProperties注释的类可能不适合扫描,例如开发自定义的配置,并希望有条件的启用他们。此类情况请使用@EnableConfigurationProperties注解要处理的class类型。需要跟 @configuration配合使用。

    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration(proxyBeanMethods = false)
    @EnableConfigurationProperties(SomeProperties.class)
    public class MyConfiguration {
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    进行配置文件扫描时,需要在应用程序增加 @ConfigurationPropertiesScan 注解,一般加在启动类上。默认情况下,从注解中声明的package进行扫描,如果需要指定特殊的package,代码如下:

    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
    
    @SpringBootApplication
    @ConfigurationPropertiesScan({ "com.example.app", "com.example.another" })
    public class MyApplication {
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    Relaxed Binding

    Spring Boot 使用宽松的规则来匹配配置文件中对的key值,跟class内定义的字段值。

    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;
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    PropertyNote
    my.main-project.person.first-nameproperties、yml文件推荐使用方式,最后的字段值自动转换为驼峰式命名
    my.main-project.person.firstName标准的驼峰式命名
    my.main-project.person.first_name.properties、yml 文件可选的数据格式之一
    MY_MAINPROJECT_PERSON_FIRSTNAME大写形式,在定义系统环境变量中推荐使用
  • 相关阅读:
    LinkedList源码深度剖析
    Solana流支付协议Zebec完成850万美元融资,CircleVentures等参投
    LTE信令流程及业务流程
    springboot+vue+elementUI 汽车车辆充电桩管理系统#毕业设计
    【Verilog基础】异步FIFO不用格雷码会影响FIFO功能吗?异步FIFO读写指针同步带来的延迟会导致设计出错吗?(面试常问)
    基于SqlSugar的开发框架循序渐进介绍(14)-- 基于Vue3+TypeScript的全局对象的注入和使用
    2023年中国一次性医用内窥镜市场发展现状分析:相关产品进入上市高峰期[图]
    第 397 场 LeetCode 周赛题解
    WeakHashMap 和 HashMap 的区别是什么,何时使用?
    基于LVM和NFS的cinder多后端
  • 原文地址:https://blog.csdn.net/u013433591/article/details/127870904