如何使Spring中带val的Kotlin数据类作为@RequestMapping参数生效
这个问题的核心原因很明确:Spring MVC在绑定请求参数到自定义对象时,默认会尝试调用对象的无参构造方法来实例化它,但你第一个定义的Params数据类全是val且没有默认值,Kotlin编译后只会生成一个包含所有参数的构造方法,没有无参构造,所以Spring无法创建实例,抛出"缺少默认构造方法"的错误。
而multiple方法能正常工作,是因为它直接接收单个@PathVariable参数,Spring不需要实例化自定义对象,自然不会有构造方法的问题。
下面给出两种优雅的解决方案,既能保留数据类的不可变性,又能让Spring正确绑定参数:
方案1:使用@ConstructorBinding(推荐,Spring Boot 2.2+)
Spring Boot 2.2引入了@ConstructorBinding注解,它告诉Spring通过类的全参构造方法来创建实例,而不是依赖无参构造。这样完美契合Kotlin数据类不可变的设计,不需要修改val字段,也不需要添加默认值。
修改后的Params类如下:
import org.springframework.boot.context.properties.ConstructorBinding import org.springframework.web.bind.annotation.PathVariable @ConstructorBinding data class Params( @PathVariable("param1") val param1: String, @PathVariable("param2") val param2: Boolean, @PathVariable("param3") val param3: Int )
这样修改后,你的single方法就可以正常接收请求了——Spring会自动把路径变量对应的值传入数据类的全参构造方法,创建不可变的Params实例。
方案2:使用Kotlin noArg编译器插件(兼容旧版本Spring)
如果你使用的是Spring Boot 2.2以下版本,或者纯Spring MVC环境,可以借助Kotlin的noArg编译器插件为Params生成无参构造方法,同时Spring会通过反射为val字段赋值(虽然Kotlin语义上val是不可变的,但Spring可以通过反射绕过访问限制完成绑定)。
配置步骤:
- 在你的构建脚本(比如Gradle Kotlin DSL)中添加
noArg插件:
plugins { kotlin("jvm") version "1.9.20" // 替换为你的Kotlin版本 kotlin("plugin.spring") version "1.9.20" kotlin("plugin.noarg") version "1.9.20" } kotlin { noarg { // 指定需要生成无参构造的类,这里直接用你的Params类全限定名 annotation("com.yourpackage.Params") // 或者如果多个类需要,可以用自定义注解标记,然后指定注解类 } }
- 保持你最初的
Params数据类定义不变即可:
data class Params( @PathVariable("param1") val param1: String, @PathVariable("param2") val param2: Boolean, @PathVariable("param3") val param3: Int )
插件会在编译时自动为Params生成无参构造方法,Spring就能正常实例化并绑定参数了。
为什么不推荐用var+默认值的方案?
你提到的改用var加默认值的方式虽然能运行,但会失去Kotlin数据类的核心优势——不可变性。不可变对象不仅线程安全,还能避免意外修改字段值导致的bug,所以尽量不要用这种妥协方案。
内容的提问来源于stack exchange,提问作者Daniel K.

