Spring Boot 3.x使用ContextConfiguration时如何修复TypeConverterSupport问题?
问题描述
使用以下基础YAML配置时:
myThing: someValue: 10
在测试中通过@ContextConfiguration启动上下文时,会触发TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:80)相关的类型转换异常,涉及字符串转Integer、Integer转String、字符串转LocalDate等操作。但改用@SpringBootTest加载完整Spring Boot上下文时,所有转换都能正常完成。推测是@ContextConfiguration中缺少实现类型转换和配置解析所需的类,但无法定位具体组件。
相关代码
MyThing组件类
package com.example.demo; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component public class MyThing { @Value("${myThing.myValue}") private Integer myValue; public Integer getMyValue() { return myValue; } }
测试类
package com.example.demo; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import static org.junit.jupiter.api.Assertions.assertEquals; @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = {MyThing.class}) // 无法正常加载上下文 class DemoApplicationTests { @Autowired private MyThing myThing; @Test void contextLoads() { assertEquals(10, myThing.getMyValue()); } }
对应YAML配置
myThing: myValue: 10
报错信息
Caused by: java.lang.NumberFormatException: For input string: "${myThing.myValue}"
at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67)
at java.base/java.lang.Integer.parseInt(Integer.java:654)
at java.base/java.lang.Integer.valueOf(Integer.java:999)
at org.springframework.util.NumberUtils.parseNumber(NumberUtils.java:203)
at org.springframework.beans.propertyeditors.CustomNumberEditor.setAsText(CustomNumberEditor.java:115)
at org.springframework.beans.TypeConverterDelegate.doConvertTextValue(TypeConverterDelegate.java:439)
at org.springframework.beans.TypeConverterDelegate.doConvertValue(TypeConverterDelegate.java:412)
at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:161)
at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:80)
... 39 more
解决方案
问题根源是仅加载MyThing.class的上下文过于基础,缺少Spring Boot自动配置的核心组件:一是解析${...}占位符的配置属性处理器,二是处理类型转换的服务组件。提供两种修复方式:
方式一:加载Spring Boot主配置类
直接在测试类中指定Spring Boot的主应用配置类(如DemoApplication),它会自动引入所有必要的配置组件:
@ExtendWith(SpringExtension.class) @ContextConfiguration(classes = {DemoApplication.class}) class DemoApplicationTests { // 测试代码保持不变 }
或者直接使用@SpringBootTest注解,它会默认加载完整的Spring Boot上下文:
@SpringBootTest class DemoApplicationTests { // 测试代码保持不变 }
方式二:手动添加必要配置组件
如果不想加载完整Spring Boot上下文,可以手动创建测试配置类,引入占位符解析器和类型转换组件:
package com.example.demo; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; import org.springframework.core.io.ClassPathResource; import org.springframework.format.FormatterRegistry; import org.springframework.format.datetime.standard.DateTimeFormatterRegistrar; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class TestConfig { // 配置占位符解析器,加载YAML配置文件 @Bean public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() { PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer(); configurer.setLocations(new ClassPathResource("application.yml")); return configurer; } // 注册LocalDate等日期类型的格式化转换器 @Bean public WebMvcConfigurer webMvcConfigurer() { return new WebMvcConfigurer() { @Override public void addFormatters(FormatterRegistry registry) { DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar(); registrar.setUseIsoFormat(true); registrar.registerFormatters(registry); } }; } }
然后在测试类中引入该配置类:
@ExtendWith(SpringExtension.class) @ContextConfiguration(classes = {MyThing.class, TestConfig.class}) class DemoApplicationTests { // 测试代码保持不变 }
关键说明
PropertySourcesPlaceholderConfigurer是解析${...}占位符的核心组件,没有它,Spring会直接把占位符字符串当作字段值,导致类型转换失败。- Spring Boot的自动配置会默认引入这类组件,而单纯的
@ContextConfiguration只会加载指定类,不会自动添加这些核心配置。 - 对于LocalDate等日期类型,需要通过
DateTimeFormatterRegistrar或自定义转换器注册格式化规则,才能完成字符串到日期的转换。
内容的提问来源于stack exchange,提问作者buddyp450

