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

Spring 6.0.9升级至6.1.3触发Bean创建异常求助

问题描述

为修复CVE-2024-22233漏洞,将Spring版本从6.0.9升级至6.1.3,修改build.gradle的版本号如下:

diff --git a/build.gradle b/build.gradle
index 08e30392f5..2aa311df7e 100644
--- a/build.gradle
+++ b/build.gradle
@@ -19,7 +19,7 @@ ext {
 
     // Dependency versions
     codenarcVersion = '3.2.0'
-    springVersion = '6.0.9'
+    springVersion = '6.1.3'
     groovyAllVersion = '3.0.17'
     bouncyCastleVersion = '1.70'
     log4jVersion = '2.20.0'

构建测试应用时,加载名为cabinetFinder(类型com.simplicity.domain.meta.Cabinet)的Bean失败,报错如下:

Unsatisfied dependency expressed through constructor parameter 0: Could not convert argument value of type [com.simplicity.domain.meta.Characteristics] to required type [java.util.Map]: Failed to convert value of type 'com.simplicity.domain.meta.Characteristics' to required type 'java.util.Map'; Cannot convert value of type 'com.simplicity.domain.meta.Characteristics' to required type 'java.util.Map': no matching editors or conversion strategy found

XML Bean定义中按名称指定构造参数,但Spring 6.1.3似乎改为按参数索引映射,导致Characteristics对象被传入需要Map类型的第一个构造参数,该问题在Spring 6.0.9中不存在。

需明确以下问题:

  • 是否遗漏必要配置?
  • 除Spring版本外还需修改build.gradle的哪些内容?
  • 这属于Spring的问题吗?

相关代码及配置

Java代码

// SimplicityCachingManager.java
@ContextConfiguration(locations = { "classpath*:/META-INF/**/*SpringConfiguration.groovy",
        "classpath*:/META-INF/**/applicationContext-*.xml" })
@RunWith(SpringJUnit4ClassRunner.class)
public abstract class AbstractSpringCachingTest {
}

public class SimplicityCachingManager extends AbstractSpringCachingTest {
    @Resource
    protected SimplicityFactory simplicityFactory;
}

// SimplicityFactoryIntegUtils.java
public class SimplicityFactoryIntegUtils {
    @Resource
    private SimplicityFactory simplicityFactory;
}

// SimplicityFactory.java
public class SimplicityFactory {
    @Autowired
    private ApplicationContext applicationContext;

    public void initInstance() {
        registerCabinets(this.applicationContext.getBeansOfType(Cabinet.class));
    }
}

// Cabinet.java
import org.springframework.context.ApplicationContext;

public class Cabinet {
    @Autowired
    private ApplicationContext applicationContext;

    public Cabinet(final Map<String, Map<CabinetEnum, String>> oldCabinets, final int cabinetRevisionNo, final int categoryId,
            final DescriptorModel descriptorModel, @Nullable final String facadeType, @Nullable final String label,
            final SecurityLevel securityLevel, final DeliveryInfo deliveryInfo,
            final Map<String, SimpleContainer> simpleContainers,
            final Map<String, ComplexContainer> complexContainers,
            final Map<String, Map<CabinetEnum, String>> manufacturer, final int maxDoors,
            final Characteristics characteristics, final Set<String> cabinetsToBeExcluded,
            final Set<String> manufacturerToBeExcluded, final Set<String> knownModelNames,
            final boolean defaultCabinet, final boolean alternateModel) {
    }
}

// MetaCabinetSpringConfiguration.java
@Configuration
public class MetaCabinetSpringConfiguration {

    @Bean(name = "cabinetCharacteristics")
    public Characteristics cabinetCharacteristics() {
        return createCharacteristics(false, Functionality.SINGLE);
    }

    public Characteristics createCharacteristics(final boolean color, final Functionality functionality) {
        return new Characteristics(Generation.NA, color, functionality, MaximumSize.NA);
    }
}

// Characteristics.java
public class Characteristics {
    public Characteristics(final Generation generation, final boolean color, 
           final Functionality functionality, final MaximumSize maximumSize) {
    }
}

XML配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">

    <!-- applicationContext-definition-1.xml -->
    <bean id="simplicityFactoryIntegUtils" class="com.simplicity.domain.meta.SimplicityFactoryIntegUtils" />

    <!-- applicationContext-definition-2.xml -->
    <bean id="simplicityFactory" class="com.simplicity.domain.meta.SimplicityFactory" init-method="initInstance"/>

    <!-- applicationContext-metaCabinetFinder.xml -->
    <bean id='cabinetFinder' parent='moamp'>
        <constructor-arg name='simpleContainers'><map merge='true'/></constructor-arg>
        <constructor-arg name='descriptorModel' ref='descriptorModel-generic' />
        <constructor-arg name="knownModelNames">
            <set>
                <value>dummyModelName</value>
            </set>
        </constructor-arg>
        <constructor-arg name='defaultCabinet' value='true' />
    </bean>

    <!-- applicationContext-moamp.xml -->
    <bean id='moamp' abstract='true' class="com.simplicity.domain.meta.Cabinet">
        <constructor-arg name='characteristics' ref='cabinetCharacteristics'/>
        <constructor-arg name='manufacturer'><map merge='true'/></constructor-arg>
        <constructor-arg name='oldCabinets'><map merge="true"/></constructor-arg>
        <constructor-arg name="complexContainers"><map merge="true" /></constructor-arg>
        <constructor-arg name='deliveryInfo' ref='infodev'/>
        <constructor-arg name='maxDoors' value='0'/>
        <constructor-arg name='categoryId' value='-1' />
        <constructor-arg name='cabinetRevisionNo' value='-1' />
        <constructor-arg name='facadeType'><null /></constructor-arg>
        <constructor-arg name='label'><null /></constructor-arg>
        <constructor-arg name='securityLevel' value='NONE'/>
        <constructor-arg name='simpleContainers'><map merge='true'/></constructor-arg>
        <constructor-arg name="cabinetsToBeExcluded"><set /></constructor-arg>
        <constructor-arg name="manufacturerToBeExcluded"><set /></constructor-arg>
        <constructor-arg name="knownModelNames"><set /></constructor-arg>
        <constructor-arg name="alternateModel" value="false" />
    </bean>
    <bean id='infodev' class='com.simplicity.domain.delivery.DeliveryInfo'>
        <constructor-arg name='supportedMethods'><map /></constructor-arg>
        <constructor-arg name='devId'><null /></constructor-arg>
    </bean>  
</beans>

解答

问题根源

Spring 6.1.x对XML构造参数名称匹配逻辑做了调整,核心原因:

  1. Cabinet类构造参数的名称在编译时未被保留(比如未开启-parameters编译选项,或字节码被混淆),导致Spring无法通过名称匹配, fallback 到按索引匹配构造参数。
  2. 抽象Beanmoamp中的构造参数配置顺序,与Cabinet构造函数的参数顺序完全不一致,按索引匹配时就会出现类型不匹配——比如moamp第一个配置的是characteristics,但Cabinet构造函数第一个参数是oldCabinets(Map类型),最终触发类型转换错误。

解决方案

方案1:开启编译参数保留参数名(推荐)

在build.gradle中添加编译配置,让Java编译器保留构造函数参数名,Spring就能正确按名称匹配参数:

tasks.withType(JavaCompile) {
    options.compilerArgs.add('-parameters')
}

此方案无需修改现有XML配置,兼容性最好。

方案2:调整XML构造参数顺序匹配构造函数

修改抽象Beanmoamp的<constructor-arg>顺序,严格对应Cabinet构造函数的参数顺序:

<bean id='moamp' abstract='true' class="com.simplicity.domain.meta.Cabinet">
    <!-- 严格对应Cabinet构造函数参数顺序 -->
    <constructor-arg name='oldCabinets'><map merge="true"/></constructor-arg>
    <constructor-arg name='cabinetRevisionNo' value='-1' />
    <constructor-arg name='categoryId' value='-1' />
    <constructor-arg name='descriptorModel' ref='descriptorModel-generic'/>
    <constructor-arg name='facadeType'><null /></constructor-arg>
    <constructor-arg name='label'><null /></constructor-arg>
    <constructor-arg name='securityLevel' value='NONE'/>
    <constructor-arg name='deliveryInfo' ref='infodev'/>
    <constructor-arg name='simpleContainers'><map merge='true'/></constructor-arg>
    <constructor-arg name='complexContainers'><map merge="true" /></constructor-arg>
    <constructor-arg name='manufacturer'><map merge='true'/></constructor-arg>
    <constructor-arg name='maxDoors' value='0'/>
    <constructor-arg name='characteristics' ref='cabinetCharacteristics'/>
    <constructor-arg name="cabinetsToBeExcluded"><set /></constructor-arg>
    <constructor-arg name="manufacturerToBeExcluded"><set /></constructor-arg>
    <constructor-arg name="knownModelNames"><set /></constructor-arg>
    <constructor-arg name='defaultCabinet' value='false'/>
    <constructor-arg name="alternateModel" value="false" />
</bean>

方案3:显式指定构造参数索引

在XML的<constructor-arg>中添加index属性,明确参数对应的构造函数索引(索引从0开始):

<constructor-arg index='12' name='characteristics' ref='cabinetCharacteristics'/>

此方式维护成本高,参数顺序变化时需同步修改索引,不推荐。

额外检查项

  • 确保所有Spring核心依赖(spring-core、spring-context等)都使用6.1.3版本,避免混合版本导致的兼容问题。
  • 检查Groovy版本(当前3.0.17)是否兼容Spring 6.1.x,可根据Spring官方兼容性文档调整版本。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 04:57:02