Spring Kotlin接口枚举参数转换:默认失效原因与解决咨询
问题描述
现有以下Kotlin代码:
Controller代码
@RestController("/test") class MyController( private val myService: MyService ) { @RequestMapping(value = ["/test"], method = [ RequestMethod.POST, RequestMethod.GET ]) fun redirect(myDto: MyDto) = ModelAndView("redirect:".plus(myService.getUrlFrom(myDto))) }
DTO代码
class MyDto( prop1: MyEnum, prop2: String, prop3: MySecondEnum )
枚举代码
enum class MyEnum(val value: String) { VALUE_1("value1"), VALUE_2("value2"), VALUE_3("another_value") } enum class MySecondEnum(val value: String) { A_VALUE("a"), B_VALUE("b"), C_VALUE("c") }
期望处理请求:GET /test/test?prop1=another_value&prop2=anything&prop3=a,但默认情况下请求无法正常工作,需解答:
- 为什么默认不生效?
- 该如何解决?
- 是否需要创建全局转换器?
原因分析
Spring默认的枚举参数绑定逻辑是根据枚举的名称(如VALUE_3)进行匹配,而非你自定义的value字段值(如another_value)。当请求传入prop1=another_value时,Spring找不到名称为another_value的MyEnum实例,同理prop3=a也找不到名称为a的MySecondEnum实例,最终导致参数绑定失败,请求无法正常处理。
解决方案
有几种可行的解决方式,可根据场景选择:
方式一:自定义全局枚举转换器(推荐)
创建针对目标枚举的Converter实现,注册到Spring容器后,全局生效所有该类型的参数绑定:
import org.springframework.core.convert.converter.Converter class MyEnumConverter : Converter<String, MyEnum> { override fun convert(source: String): MyEnum? { return MyEnum.values().firstOrNull { it.value == source } } } class MySecondEnumConverter : Converter<String, MySecondEnum> { override fun convert(source: String): MySecondEnum? { return MySecondEnum.values().firstOrNull { it.value == source } } }
注册转换器到Spring配置:
import org.springframework.context.annotation.Configuration import org.springframework.format.FormatterRegistry import org.springframework.web.servlet.config.annotation.WebMvcConfigurer @Configuration class WebConfig : WebMvcConfigurer { override fun addFormatters(registry: FormatterRegistry) { registry.addConverter(MyEnumConverter()) registry.addConverter(MySecondEnumConverter()) } }
方式二:枚举添加静态匹配方法,手动转换参数
在枚举类中添加静态方法用于根据自定义value查找实例,然后在Controller中手动转换参数:
修改枚举类:
enum class MyEnum(val value: String) { VALUE_1("value1"), VALUE_2("value2"), VALUE_3("another_value"); companion object { fun fromValue(value: String): MyEnum? { return values().firstOrNull { it.value == value } } } } enum class MySecondEnum(val value: String) { A_VALUE("a"), B_VALUE("b"), C_VALUE("c"); companion object { fun fromValue(value: String): MySecondEnum? { return values().firstOrNull { it.value == value } } } }
修改Controller方法:
@RequestMapping(value = ["/test"], method = [ RequestMethod.POST, RequestMethod.GET ]) fun redirect( @RequestParam("prop1") prop1: String, @RequestParam("prop2") prop2: String, @RequestParam("prop3") prop3: String ): ModelAndView { val myDto = MyDto( MyEnum.fromValue(prop1) ?: throw IllegalArgumentException("Invalid prop1 value"), prop2, MySecondEnum.fromValue(prop3) ?: throw IllegalArgumentException("Invalid prop3 value") ) return ModelAndView("redirect:".plus(myService.getUrlFrom(myDto))) }
方式三:使用@Convert注解(局部生效)
如果仅需在特定DTO中生效绑定逻辑,可结合@Convert注解和自定义Converter:
先创建如方式一的Converter,再修改DTO:
class MyDto( @Convert(converter = MyEnumConverter::class) val prop1: MyEnum, val prop2: String, @Convert(converter = MySecondEnumConverter::class) val prop3: MySecondEnum )
是否需要创建全局转换器?
如果项目中多个接口、多个DTO都需要根据自定义value绑定这两个枚举,推荐创建全局转换器,避免重复编写绑定逻辑,代码更简洁统一。如果仅当前接口需要处理这类枚举参数,也可以选择方式二或方式三,按需取舍即可。
内容的提问来源于stack exchange,提问作者Ugo Evola
相关产品推荐
相关产品推荐

