Spring Boot中多个application配置文件在属性覆盖时List和Map的不同处理行为

在spring boot 项目中,经常会使用profile来控制不同环境的属性设置及覆盖行为,即通过高优先级的配置文件来覆盖默认的属性,以达到测试/生产环境配置生效的目的. 常规的字符串,bean属性在覆盖时均会通过标准的merge覆盖过程,达到优先查找高优先级的配置文件,如果存在,则使用此值,并且不再继续继续处理.

但如果一个属性为List或Map属性,更或者List,Map中的值对象更是一个复杂类型时,其处理过程就比较麻烦。详见

https://github.com/spring-projects/spring-boot/issues/4313
https://github.com/spring-projects/spring-boot/issues/9137

为处理此问题,最终spring boot标准化了针对List或Map类型的处理规则。详见:

https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-external-config-complex-type-merge

本文为翻译原文配置中的信息

当List属性在多个配置文件中生效时,那么重写规则将会覆盖整个对象,而不是合并.

如 类 MyPojo, 属性分别为 name,description, 其默认值为 null. 以下类为对象类 AcmeProperties 有一个 List<MyPojo> 的属性.

@ConfigurationProperties("acme")
public class AcmeProperties {
    private final List<MyPojo> list = new ArrayList<>();

    public List<MyPojo> getList() {
        return this.list;
    }
}

配置举例:

acme:
  list:
    - name: my name
      description: my description
-------- 分隔线
spring:
  profiles: dev
acme:
  list:
    - name: my another name

如果 dev profile 没有启用时, AcmeProperties.list 只会包含一个对象。如果 dev profile 启用时,也仍然只有一个对象。惟一的区别在于,启用时其myPojo对象中的name值为 (my another name), description值为null. 启用dev profile并不会再添加一个 MyPojo 对象 到集合中, 即不会作整合操作.

当 List 配置在多个配置文件中时,最高优先级的配置文件中的配置信息即会生效,并且只处理此配置文件值。 如下例所示:

acme:
  list:
    - name: my name
      description: my description
    - name: another name
      description: another description
-------- 分隔线
spring:
  profiles: dev
acme:
  list:
    - name: my another name

如上例子,如果 dev profile 启用时, AcmeProperties.list 中只会包含一个对象,其name属性值为 (my another name), description值为null. 针对 YAML 文件而言,不管是 逗号分隔的list,还是- 形式的list形式,都可以覆盖重写整个list内容集.

针对 Map 属性类型, 与 List 不同,可以在多个配置文件中配置不同的键值对。惟一的不同在于,针对相同key的 KV 值,高优先级的 V,将会生效,但不同key的 KV 值将会执行合并操作。如下,类 AcmeProperties 中存在 一个 Map<String, MyPojo> 类型的属性.

@ConfigurationProperties("acme")
public class AcmeProperties {
    private final Map<String, MyPojo> map = new HashMap<>();

    public Map<String, MyPojo> getMap() {
        return this.map;
    }
}

如下所示的配置文件.

acme:
  map:
    key1:
      name: my name 1
      description: my description 1
-------- 分隔线
spring:
  profiles: dev
acme:
  map:
    key1:
      name: dev name 1
    key2:
      name: dev name 2
      description: dev description 2

如果 dev profile 没有启用时,AcmeProperties.map 仅会包含一个 KV 值,其 K 为 key1, V 为 (name值为 my name 1, description值为 my description 1). 当 dev profile 启用时, map属性则会包含两个 KV 值. 分别为 K-> key1, V->(name:dev name1, description:my description 1), K-> key2, V->(name:dev name2, description: dev description 2).

值得注意的是,在以上的处理过程中, dev profile启用时 K->key1的V值的name和description,是 两个配置文件的合并值,而并不是单单dev配置文件中的值(因为dev 配置中 description 为null, 而 默认配置中 description 为有值,最终结果为合并值)

整个处理过程不管是 properties 文件,还是 yaml 文件,均适用于此规则.

转载请标明出处:i flym
本文地址:https://www.iflym.com/index.php/code/202001090001.html

相关文章:

作者: flym

I am flym,the master of the site:)

发表评论

电子邮件地址不会被公开。 必填项已用*标注