一文学会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;

}

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

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

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

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

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

获取命令行属性值

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

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

SpringApplication.setAddCommandLineProperties(false)

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\"}'

外部属性文件

系统启动时,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

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

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

大多数情况下,每个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

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

Importing Additional Data

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

spring:
  application:
    name: "myapp"
  config:
    import: "optional:file:./dev.properties"

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

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

spring:
  config:
    import: "my.properties"
my:
  property: "value"
my:
  property: "value"
spring:
  config:
    import: "my.properties"

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

Importing Extensionless Files

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

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

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

Using Configuration Trees

etc/
  config/
    myapp/
      username
      password

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

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

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

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

etc/
  config/
    dbconfig/
      db/
        username
        password
    mqconfig/
      mq/
        username
        password
spring:
  config:
    import: "optional:configtree:/etc/config/*/"

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}"

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"

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

spring.application.name=MyApp
#---
spring.application.name=MyCloudApp
spring.config.activate.on-cloud-platform=kubernetes

Activation Properties

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

Property Note
on-profile 表达式与配置文件匹配,才能激活相关属性
on-cloud-platform 当配置处于活动状态,CloudPlatform配置值才被有效
myprop:
  "always-set"
---
spring:
  config:
    activate:
      on-cloud-platform: "kubernetes"
      on-profile: "prod | staging"
myotherprop: "sometimes-set"

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"

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

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

my:
 servers:
 - "dev.example.com"
 - "another.example.com"

上述example将被转换为:

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

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]}"

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...

    }
}

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;
        }
    }
}

@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;
}

使用构造函数绑定,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 {

}

进行配置文件扫描时,需要在应用程序增加 @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 {

}

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;
    }

}
Property Note
my.main-project.person.first-name properties、yml文件推荐使用方式,最后的字段值自动转换为驼峰式命名
my.main-project.person.firstName 标准的驼峰式命名
my.main-project.person.first_name .properties、yml 文件可选的数据格式之一
MY_MAINPROJECT_PERSON_FIRSTNAME 大写形式,在定义系统环境变量中推荐使用

你可能感兴趣的:(java,基础,Springboot,配置文件,Springboot配置优先级,spring,配置自动绑定)