SPRING BOOT 外部化配置

因为 Spring Boot 的外部化配置特性,所以我们可以在一个项目中使用多种环境的配置。Spring Boot 提供的外部化配置的方式有 Java 属性配置文件、YAML 配置文件、环境变量和命令行参数。

配置文件中的属性值可以使用 @Value 注解直接注入,通过 Spring 的 Environment 抽象访问,或者通过 @ConfigurationProperties 绑定到结构化对象中。

Spring Boot 的配置加载有特定的顺序,按照下面的顺序,前面的配置将会被后面的配置覆盖:

  1. 默认配置(通过 SpringApplication.setDefaultProperties 设置)
  2. @PropertySource 注解的配置类(@Configuration)。注意,这种配置属性在应用上下文被刷新之前不会加载到 Environment 中。This is too late to configure certain properties such as logging.* and spring.main.* which are read before refresh begins.
  3. 配置文件(如:application.properties)
  4. 只保存在 random.* 中的 RandomValuePropertySource
  5. 系统环境变量
  6. Java 系统配置(System.getProperties())
  7. java:comp/env 中的 JNDI 属性
  8. ServletContext 初始化参数
  9. ServletConfig 初始化参数
  10. SPRING_APPLICATION_JSON (inline JSON embedded in an environment variable or system property) 中的配置属性
  11. 命令行参数
  12. 测试中的 properties 属性。 Available on @SpringBootTest and the test annotations for testing a particular slice of your application.
  13. 测试中的 @TestPropertySource 注解
  14. 在开启开发者工具时,保存在 $HOME/.config/spring-boot 目录中的开发者工具设置属性

配置文件的加载顺序:

  1. Jar 包内部的应用配置文件(application.properties 和它的 YAML 格式)
  2. Jar 包内部的 Profile-specific application propertiesapplication-{profile}.properties 和它的 YAML 格式)
  3. Jar 包外部的 Application propertiesapplication.properties 和它的 YAML 格式)
  4. Jar 包外部的 Profile-specific application propertiesapplication-{profile}.properties 和它的 YAML 格式)

Spring Boot 官方建议在同一工程中只使用一种格式的配置文件,.properties 或 .yml 格式。当两者同时存在时,.properties 格式的配置文件优先。

假设现在有个 Spring Bean 组件中使用了名为 name 的配置属性,如下:

import org.springframework.stereotype.*;
import org.springframework.beans.factory.annotation.*;

@Component
public class MyBean {

    @Value("${name}")
    private String name;

    // ...

}

此时,jar 包中 application.properties 配置文件中定义的 name 属性,将会被 jar 包外 application.properties 配置文件中定义的 name 属性覆盖。而在命令行中( java -jar app.jar --name "Spring" )也定义了 name 属性的时候,那么前两者的定义都会被覆盖。

使用命令行配置应用属性

默认情况下,SpringApplication 会将命令行中的可选参数(以 -- 开始的命令参数,例如: --server.port 9000 )转换到 property ,并将它们添加到 Spring Environment 中。前面也有提到,来自命令行的配置属性的优先级高于基于文件的配置属性。

如果不想将来自命令行的配置属性添加到 Environment 中,可以通过 SpringApplicaiton.setAddCommandLineProperties(false) ,将它禁用掉。

使用外部配置文件配置应用属性

在应用启动时, Spring Boot 会自动在下面这些位置查找并加载 application.properties 和 application.yaml 配置文件:

  1. classpath 根目录
  2. classpath 下的 /config 目录
  3. 当前目录
  4. 当前目录下的 /config 子目录
  5. /config 目录的直接子目录

上面列表是根据优先级排序的(顺序靠后的将会覆盖顺序靠前的)。已加载的配置文件作为 PropertySources 添加到 Spring Environment 中。默认的配置文件名称是 application.properties,可以通过 spring.config.name 属性的设置来修改。配置文件保存的目录可以通过 spring.config.location 属性(属性的值是以逗号分隔的目录或文件路径)的设置来修改。

修改配置文件名称:

java -jar myproject.jar --spring.config.name=myproject

修改配置文件位置:

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

如果不在意文件位置是否存在,需要在文件位置前添加 optional: 前缀。

spring.config.namespring.config.location 会在应用启动过程中比较靠前的位置,所以为了能够通过它们来确定配置文件名称和位置,要在更早的时候来设置它们,比如操作系统环境变量,Java 系统属性或者命令行参数中。

如果 spring.config.location 中所包含的目录(与文件不同)需要以 / 结尾。因为在运行加载这些目录下的配置文件之前,需要通过字符串拼接的方式将 spring.config.name 拼接为完整的配置文件路径。而 spring.config.location 中设置的配置文件路径会被原样使用。不管是直接设置配置文件的完整路径还是通过拼接的方式,它们都需要以 .properties、yaml 或者 yml 扩展名结尾。

当配置文件位置出现重复时,后面的设置将会覆盖掉前面的设置。

当使用 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 追加的配置文件位置中的配置文件中的属性可以覆盖默认配置文件位置中的属性。例如, spring.config.additional-location 设置为 optional:classpath:/custom-config/,optional:file:./custom-config/ ,那么完整的配置文件位置列表将会是:

  1. optional:classpth:/
  2. optional:classpath:/config/
  3. optional:file:./
  4. optional:file:./config/
  5. optional:file:./config/*/
  6. optional:classpath:custom-config/
  7. optional:file:./custom-config/

上面的配置文件位置列表会在启动是依次搜索,且后面的会覆盖前面的。通过这种规则,可以在默认位置外的其他位置有选择的覆盖默认位置配置文件里的配置属性值。比如在默认位置加载 application.properties 文件中的值作为默认值,而在追加的配置文件位置( optional:classpath:custom-config/optional:file:./custom-config/ )写入覆盖的值。

如果使用操作系统环境变量而不是 Java 系统属性,很多操作系统不允许使用点号(.)分隔变量名,这时可以使用下划线分隔的变量名来代替。例如:使用 SPRING_CONFIG_NAME 代替 spring.config.name 。详情参阅 Binding from Environment Variables

如果应用运行在 servlet 容器或应用服务器中,那么 JNDI 属性或 servlet 上下文初始化参数可以用来代替或同时使用操作系统环境变量或 Java 系统属性。

可选搜索位置

默认情况下,当指定的配置文件位置不存在,Spring Boot 会抛出 ConfigDataLocationNotFoundException 异常并停止。

如果不想在指定的配置文件位置不存在时抛出异常并停止时,就需要在配置文件位置之前添加 optional: 前缀。 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。

通配搜索位置

在配置文件位置路径的最后一段中使用星号( * )通配符,通配符会在加载配置文件位置时展开。这样的位置设置方式在 Kubernetes 这种有多个属性配置文件的环境中特别有用。

例如,工程中有一些 Redis 配置和一些 MySQL 配置,同时需要它们都保存在 application.properties 文件中。这时可以将它们分别保存在 /config/redis/application.properties 和 /config/mysql/application.properties 文件中,然后通过在配置文件位置设置中使用通配符来加载它们,像这样 config/*

默认情况下,Spring Boot 的默认搜索位置中包括 config/*/ ,这意味着 JAR 之外的 /config 目录下的所有子目录都会被搜索。

配置文件位置中的通配符可以在 spring.config.locationspring.config.additional-location 中使用。

通配搜索位置中只能包含一个星号( * ),如果是目录则以 / 结尾,如果是文件则以 */<filename> 结尾。带有通配符的搜索位置将根据文件名的字母顺序排序。

通配搜索位置只能用于外部目录,不能用在 classpath: 的位置。

Profile

像 application 属性配置文件那样,Spring Boot 也会尝试加载以 application-{profile} 形式命名的"特定配置"文件。例如,应用以 YAML 文件的方式启用了名为 prod 的配置文件,那么 application.yml 和 application-prod.yml 都将会被加载。

"特定配置"文件将会从标准的 application.properties 相同的位置加载,"特定配置"文件中的属性始终覆盖"非特定配置"文件中的属性。如果同时存在多个"特定配置"文件被激活,那么 Spring Boot 将会按照先加载默认配置文件,然后按照激活顺序加载"特定配置"文件,这样就把所有的配置文件中的属性全部添加到 Environment 中,其中重复项的属性值将由最后一个被加载的配置文件确定。例如,如果使用 spring.profiles.active 属性设置了 prod,live 两个"特定配置"文件,Spring Boot 的加载顺序为 application.properties、application-prod.properties、application-dev.properties,这样若出现重复项,这些重复项的值的确定将由最后的 application-dev.properties 确定。

The Environment has a set of default profiles (by default, [default]) that are used if no active profiles are set. In other words, if no profiles are explicitly activated, then properties from application-default are considered.

属性配置文件只会加载一次,如果已经直接导入了特定环境的配置文件,那么它不会再次被导入。

Importing Additional Data

Encrypting Properties

Working with YAML

Configuring Random Values

Type-safe Configuration Properties

Date: 2021-05-10 Mon 13:10

Author: xueshumeng

Email: xue.shumeng@yahoo.com

Created: 2023-08-28 Mon 14:16