在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 文件,均适用于此规则.