I tried upgrading our app's Spring version from 6.0.9 to 6.1.3 (to mitigate a CVE issue CVE-2024-22233). I change the version number of spring in build.gradle as shown below.
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'
However, when I build/test the application, I am encountering bean creation failure when spring tried to load the bean named cabinetFinder (of class com.simplicity.domain.meta.Cabinet) as shown in the stacktrace.
What happen: When spring loads cabinetFinder bean, it tries to load first its parent bean moamp. This is where the error occurred.
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
In the XML bean definition, constructor arguments are named in order to map it in its equivalent class parameters (convention over configuration).
However, Spring is trying to map constructor parameters based on parameter index. That is, in XML, characteristics is the first constructor argument but in the class it is a map. Thereby, it's complaining. This is not a problem in spring 6.0.9.
Did I miss anything? Is there anything I need to change/update in my build.gradle configuration aside from spring? Or is this something that Spring needs to look into?
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'simplicityFactory' defined in file [/Development/Projects/CabinetApp/build/resources/main/META-INF/applicationContext-definition-2.xml]: Error creating bean with name 'cabinetFinder' defined in file [/Development/Projects/CabinetApp/build/resources/main/META-INF/metaCabs/applicationContext-metaCabinetFinder.xml]: 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
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1773) ~[spring-beans-6.1.3.jar:6.1.3]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:599) ~[spring-beans-6.1.3.jar:6.1.3]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:521) ~[spring-beans-6.1.3.jar:6.1.3]
.
.
Here are the classes and XML files that are involved in this resolution error. For the sake of brevity, other classes are not included.
//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 Bean Definitions
<?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>
I need other fresh eyes to check this because it's already driving me nuts. Thank you.
I expect that the upgrade would just be seamless.