Springboot配置管理 - Externalized Configuration

通过配置文件提高代码灵活性是每一个应用都必然要考虑的现实问题,Springboot通过Externalized Configuration(外部化配置)对配置文件提供了非常灵活的支持。

Spring Boot lets you externalize your configuration so that you can work with the same application code in different environments. You can use a variety of external configuration sources including Java properties files, YAML files, environment variables, and command-line arguments.

Spring Boot允许你外部化配置文件,因此你可以让相同的代码在不同的环境下工作。你可以采用包括Java properties files、YAML files、环境变量、命令行参数等在内的多种外部化方式进行配置。

Spring Boot提供的多种外部配置优先级如下(从低到高),优先级高的配置项会覆盖掉优先级低的配置项而最终生效:

  1. Default properties: 通过SpringApplication.setDefaultProperties设置。
  2. @PropertySource注解:在@Configuration注解文件中使用,这种方式的配置文件在applicatio context刷新的时候生效。因此对于需要在容器刷新前就生效的某些配置项比如logging.* and spring.main.* ,此种方式的配置不会生效。
  3. Config data:比如application.properties配置文件等。
  4. 随机数配置器:A RandomValuePropertySource that has properties only in random.*。
  5. OS environment variables:操作系统环境变量。
  6. Java System properties :Java系统属性,通过System.getProperties()获取。
  7. JNDI attributes from java:comp/env。
  8. ServletContext init parameters。
  9. ServletConfig init parameters。
  10. Properties from SPRING_APPLICATION_JSON (inline JSON embedded in an environment variable or system property),通过环境变量或系统属性配置的JSON方式的配置信息。
  11. Command line arguments:命令行参数。
  12. properties attribute on your tests. Available on @SpringBootTest and the test annotations for testing a particular slice of your application。
  13. @DynamicPropertySource annotations in your tests。
  14. @TestPropertySource annotations on your tests。
  15. Devtools global settings properties in the $HOME/.config/spring-boot directory when devtools is active。

其中第3项中的Config data files的优先级(从低到高):

  1. Application properties packaged inside your jar (application.properties and YAML variants):jar包内的properties或yaml配置文件。
  2. Profile-specific application properties packaged inside your jar (application-{profile}.properties and YAML variants):jar包内的profile标识的properties或yaml配置文件。
  3. Application properties outside of your packaged jar (application.properties and YAML variants):jar包外的properies或yaml配置文件。
  4. Profile-specific application properties outside of your packaged jar (application-{profile}.properties and YAML variants):jar包外的profile标识的properties或yaml配置文件。

It is recommended to stick with one format for your entire application. If you have configuration files with both .properties and YAML format in the same location, .properties takes precedence.

注意建议你的Spring Boot项目采用同一种配置文件完成配置,如果项目中的同一位置同时存在.properties和.yaml文件,则.properties文件优先(yaml配置文件中的相同配置无效)。

命令行参数

Spring Boot可以读取命令行参数到配置属性中,命令行参数通过 – 符号设置,比如 --server.port=8080。命令行参数比配置文件具有更高的优先级。

通过SpringApplication.setAddCommandLineProperties(false)可以禁用命令行参数。

JSON Application Properties

以下三种方式设置:

  1. 操作系统环境变量:

$ SPRING_APPLICATION_JSON=‘{“my”:{“name”:“test”}}’ java -jar myapp.jar

  1. java 系统属性:

$ java -Dspring.application.json=‘{“my”:{“name”:“test”}}’ -jar myapp.jar

  1. 通过命令行参数:

$ java -jar myapp.jar --spring.application.json=‘{“my”:{“name”:“test”}}’

External Application Properties

Spring Boot启动过程中从如下路径自动调用application.properties或application.yaml文件:

  1. 从classpath调用
    a. classpath根目录
    b. classpath下的 /config目录
  2. 从当前目录调用
    a. 当前目录
    b. 当前目录下的 /config目录
    c. /config目录下的子目录
    以上顺序是Spring Boot加载配置文件的优先级(由低到高),后面的配置文件会覆盖掉前面的配置文件。配置文件读取后会被加载到会被加载的Spring Environment的PropertySources 中。

application是默认的配置文件名,可以通过如下方式修改:

$ 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表示当前配置是可选的,指定位置不存在该配置文件,系统也不会报错。否则不指定optional、配置文件不存在的话,Spring Boot启动过程中会抛出ConfigDataLocationNotFoundException异常。

全局变量spring.config.on-not-found设置为ignore,可以让Spring Boot忽略所有的配置文件不存在异常、继续启动系统:

SpringApplication.setDefaultProperties(…​) or with a system/environment variable.

spring.config.name, spring.config.location, and spring.config.additional-location等三个参数在Spring Boot项目启动的早期就会被调用,所以,必须通过环境属性指定、而不能通过配置文件的方式指定。

spring.config.location支持profile。

Wildcard Locations

带 * 的配置文件位置的含义是:Spring Boot读取配置文件的时候,指定位置的子目录也被包含在内。对于有多个配置文件的应用,* 配置方式会比较方便。

默认情况下,Spring Boot会包含 config/*/ 作为其默认的配置文件搜索路径。意思是jar包下的config路径及其子目录下的配置文件都会生效。

可以在spring.config.location或spring.config.additional-location参数中使用 * 指定配置文件。

Profile Specific Files

这一特性主要是为了支持应用在多个环境下采用不同配置参数的场景,比如开发环境、测试环境、生产环境,同一参数在这三个不同的环境下的参数配置不同,如果在系统部署的时候手工切换(相信之前有好多项目确实就是这么干的),很有可能会由于操作失误导致严重问题。

Spring Boot官网描述的很清楚:

Profile-specific properties are loaded from the same locations as standard application.properties, with profile-specific files always overriding the non-specific ones. If several profiles are specified, a last-wins strategy applies. For example, if profiles prod,live are specified by the spring.profiles.active property, values in application-prod.properties can be overridden by those in application-live.properties.

通过spring.profiles.active参数指定profile后,相同位置存在多个相同配置文件(名字相同、profile不同)的情况下,指定的profile文件会生效。比如,spring.profiles.active指定为prod,live,当前路径下如果同时存在application.yml、application-prod.yml、application-live.yml,那么,如果三个配置文件中都存在同一项配置,则application-live.yml中的配置胜出(会生效)。

以下描述了“后来者胜出”策略的生效方式,通过spring.config.location参数指定配置文件位置的情况下,有“同一配置组”和“不同配置组”的区别:

The last-wins strategy applies at the location group level. A spring.config.location of classpath:/cfg/,classpath:/ext/ will not have the same override rules as classpath:/cfg/;classpath:/ext/.
For example, continuing our prod,live example above, we might have the following files:
/cfg
application-live.properties
/ext
application-live.properties
application-prod.properties
When we have a spring.config.location of classpath:/cfg/,classpath:/ext/ we process all /cfg files before all /ext files:

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

When we have classpath:/cfg/;classpath:/ext/ instead (with a ; delimiter) we process /cfg and /ext at the same level:
1./ext/application-prod.properties
2./cfg/application-live.properties
3./ext/application-live.properties

建议尽可能用最简单的方式进行配置:指定profile的情况下,尽可能不将同一类型的(比如某一三方API连接参数)、不同环境(比如测试、开发、生产)的配置文件放置在不同的路径下,以免引起误伤!

Importing Additional Data

Spring Boot支持通过spring.config.import定义配置文件路径,Spring Boot启动过程中从指定路径导入配置内容。

比如在application.properties文件中指定:

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

则当前路径下如果存在dev.properties文件的话,Spring Boot会导入该文件的配置内容。并且,dev.properties配置文件中的内容优先级高于定义他的文件(上例中的application.properties)。以上案例中如果dev.properties文件中存在spring.application.name,会覆盖掉application.properties文件中的值myapp。

以上案例中,Spring Boot会首先读取application.properties文件中的配置内容,之后读取dev.properties文件中的配置内容并覆盖掉application.properties文件中的相同配置内容。因此,application.properties文件中spring.config.import的定义位置无关紧要,不管放在spring.application.name的前面、还是后面,都会覆盖掉application.properties文件中的spring.application.name值。

spring.config.import配置支持profile。

Importing Extensionless Files

有些应用的配置文件没有扩展名,Spring Boot可以通过 [ ] 支持导入。

比如配置文件是/etc/config/myconfig,文件格式是yaml,则可以配置为:

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

Using Configuration Trees

通过configtree关键字,指定路径下的文件名作为key、文件内容作为values导入Spring Boot Environment的properties中。

比如:

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

/etc/config/myapp路径下有username和password两个文件:

etc/
config/
myapp/
username
password

则myapp.username和myapp.password 将会注入到Environment的properties中。

Property Placeholders

参数可以通过占位符配置,比如:

app.name=MyApp
app.description=${app.name} is a Spring Boot application written by ${username:Unknown}

占位符app.name会被MyApp替代,如果系统中没有username这个配置值的话,系统会以冒号 : 后的值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

后面的配置文件内容将会覆盖掉前面配置文件的,上述案例的spring.application.name取值为MyCloudApp。

Activation Properties

通过spring.config.activate.*参数设置配置文件的内容仅在指定的激活条件下生效,Spring Boot支持如下激活条件:

  1. on-profile:仅针对指定的profile生效。
  2. on-cloud-platform:仅针对指定的云平台参数生效。

比如:

myprop=always-set
#---
spring.config.activate.on-cloud-platform=kubernetes
spring.config.activate.on-profile=prod | staging
myotherprop=sometimes-set

#—下面的(第二个逻辑文件内容)仅针对云平台kubernetes、以及profile为 prod或者staging生效。

Working With YAML

YAML格式的配置文件需要SnakeYAML包的支持,spring-boot-starter自动包含了SnakeYAML包,所以Spring Boot项目是默认支持YAML配置文件的。

Type-safe Configuration Properties

Spring Boot提供两种不同的配置参数绑定方式,一种是通过@Value注解,另外一种是Type-safe Configuration Properties:
Springboot配置管理 - Externalized Configuration_第1张图片
@Value注解使用起来比较方便,但是个人认为不利于代码管理,因为配置参数的绑定会分散在代码各处。

  1. 绑定到JavaBean属性:通过@ConfigurationProperties(“my.service”)注解直接绑定打JavaBean的属性上。
  2. 通过构造器绑定:通过@ConfigurationProperties(“my.service”)注解、构造器参数完成绑定。
  3. 通过@EnableConfigurationProperties(SomeProperties.class)注解、指定特定的java类完成绑定。
  4. 通过@ConfigurationPropertiesScan注解,采用配置文件属性扫描的方式完成绑定(类似于IoC容器注入bean是的组件扫描机制)。
  5. 通过@Configuration、 @ConfigurationProperties以及@Bean结合的方式完成绑定。

Relaxed Binding

Spring Boot的Relaxed Binding- 宽松绑定看,指的是SpringBoot在绑定配置文件参数的java配置类的属性上的时候,提供一种宽松的匹配规则,在配置文件中的配置和javabean中的属性名称不是完全一一对应的情况下,也可以实现属性绑定。

比如:

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

}

在宽松绑定规则下,如下配置均可绑定:
Springboot配置管理 - Externalized Configuration_第2张图片
Spring Boot官网指定的宽松绑定规则:
Springboot配置管理 - Externalized Configuration_第3张图片
以上,关于Sprng Boot配置文件的内容绝大部分来之与Spring Boot官网,Spring Boot提供了非常强大、灵活的配置文件绑定方式,但是建议大家在实际项目中采用最基础的、最简单的方式进行配置,不冲突、无歧义、能实现目标的前提条件下,越简单越好。

你可能感兴趣的:(spring,boot,后端,java)