You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot中@ConfigurationProperties嵌套集合绑定异常问题

Spring Boot配置属性绑定:集合元素属性无法跨配置源合并问题

在Spring Boot 2.6.4中,使用@ConfigurationProperties结合@ConstructorBinding的Kotlin数据类时,遇到集合类型字段的元素属性无法跨配置源合并的问题:多个配置源分别配置集合第一个元素的不同属性,但绑定到类实例时,该元素仅能获取单个配置源的值,而嵌套类字段则能正常合并。同时Environment可以正确读取合并后的属性值。

Kotlin配置类

@ConfigurationProperties("tpp.test.root")
@ConstructorBinding
data class RootPropperties(
   var rootField1: String = "",
   var rootField2: String = "",
   var nested: NestedProperties = NestedProperties(),
   var nestedList: List<NestedListProperties> = listOf()
) {

data class NestedProperties(
    var nestedField1: String = "",
    var nestedField2: String = ""
)

@ConstructorBinding
data class NestedListProperties(
    var nestedListField1: String = "",
    var nestedListField2: String = ""
)
}

配置文件

application.yml

tpp:
  test:
    root:
      root-field1: default
      nested:
        nested-field1: default
      nested-list:
        - nested-list-field1: default

application-dev1.yml

tpp:
  test:
    root:
     root-field2: dev1
     nested:
       nested-field2: dev1
     nested-list:
       - nested-list-field2: dev1

测试代码

@ActiveProfiles("dev1")
@SpringBootTest
internal class ConfigurationPropertiesTest {

@Autowired
lateinit var environment: Environment

@Autowired
lateinit var rootPropperties: RootPropperties

    @Test
    fun `configuration properties binding`() {
         Assertions.assertEquals("default", rootPropperties.rootField1)
         Assertions.assertEquals("dev1", rootPropperties.rootField2)

         Assertions.assertEquals("default", rootPropperties.nested.nestedField1)
         Assertions.assertEquals("dev1", rootPropperties.nested.nestedField2)

         Assertions.assertTrue(rootPropperties.nestedList.isNotEmpty())
         // 断言失败:Expected :default,Actual   :
         Assertions.assertEquals("default", rootPropperties.nestedList[0].nestedListField1)
         Assertions.assertEquals("dev1", rootPropperties.nestedList[0].nestedListField2)
    }

     @Test
     fun `environment binding`() {
        Assertions.assertEquals("default", environment.getProperty("tpp.test.root.root-field1"))
        Assertions.assertEquals("dev1", environment.getProperty("tpp.test.root.root-field2"))

        Assertions.assertEquals("default", environment.getProperty("tpp.test.root.nested.nested-field1"))
        Assertions.assertEquals("dev1", environment.getProperty("tpp.test.root.nested.nested-field2"))

        Assertions.assertEquals("default", environment.getProperty("tpp.test.root.nested-list[0].nested-list-field1"))
        Assertions.assertEquals("dev1", environment.getProperty("tpp.test.root.nested-list[0].nested-list-field2"))
    }
}

问题现象

  • 针对RootPropperties的测试在集合元素属性断言处失败,rootPropperties.nestedList[0].nestedListField1为空值,其余非集合相关断言均通过。
  • 针对Environment的测试全部通过,能正确获取跨配置源合并后的集合元素属性值。

原因及解决方案

原因

使用@ConstructorBinding时,Spring Boot的配置属性绑定逻辑默认会替换整个集合对象,而非合并集合内元素的属性。对于嵌套类这种单个对象,绑定逻辑会合并其属性;但集合元素会被视为独立对象,后加载的配置源中的集合定义会覆盖之前的,导致元素仅保留最后一个配置源的属性设置。

解决方案

  1. 移除集合元素类的@ConstructorBinding
    删除NestedListProperties上的@ConstructorBinding注解,改用默认的setter绑定方式,这样Spring Boot会对集合中的元素进行属性合并,而非替换整个元素:

    data class NestedListProperties(
        var nestedListField1: String = "",
        var nestedListField2: String = ""
    )
    
  2. 确保配置属性类被扫描
    在Spring Boot主类上添加@ConfigurationPropertiesScan注解,保证配置属性类能被Spring正确识别并绑定。

  3. 构造函数绑定的替代方案(若必须保留)
    如果业务要求必须使用@ConstructorBinding,则需手动实现集合元素的合并逻辑,或者将集合元素的所有属性集中在单个配置源中定义,避免跨配置源拆分属性配置。

内容的提问来源于stack exchange,提问作者typik89

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 22:24:28