Kotlin Spring Validation校验List<Pair>内自定义注解不生效
问题根因
这不是框架Bug,属于编码疏漏,核心原因是Bean Validation的级联校验规则和KotlinPair类型的适配问题:
- 你在
signaturesInfo字段上加的@get:Valid作用是触发集合元素的级联校验,但Bean Validation默认只对内置支持的容器类型(比如List、Set、Map、Optional)自动提取内部元素做校验,不会自动解析Kotlin标准库的Pair类的内部属性。 - 校验器遍历
MutableList<Pair<SignatureInfo, Object>>时,只能拿到Pair类型的元素,而Pair类本身没有声明任何校验注解,也没有对应的规则告诉校验器要深入解析first、second属性,因此包裹在Pair.first里的SignatureInfo对象上的@PkiSignWithBusinessCode注解根本没有被执行,最终返回0个校验违规。
修复方案
你可以任选以下一种方案修复:
方案1:注册Pair的值提取器(改动最小)
通过Bean Validation的ValueExtractor扩展点,告诉校验器如何提取Pair内部的元素参与校验:
import javax.validation.valueextraction.ExtractedValue import javax.validation.valueextraction.UnwrapByDefault import javax.validation.valueextraction.ValueExtractor // 提取Pair.first位置的元素参与校验 @UnwrapByDefault class PairFirstExtractor : ValueExtractor<@ExtractedValue Pair<*, *>> { override fun extractValues(value: Pair<*, *>?, receiver: ValueExtractor.ValueReceiver) { value?.apply { receiver.value("first", first) } } } // 如需校验Pair.second位置的元素可添加此提取器 @UnwrapByDefault class PairSecondExtractor : ValueExtractor<@ExtractedValue Pair<*, *>> { override fun extractValues(value: Pair<*, *>?, receiver: ValueExtractor.ValueReceiver) { value?.apply { receiver.value("second", second) } } }
注册方式分两种场景:
- 普通Java/Kotlin环境:通过Java SPI注册,在
resources/META-INF/services/javax.validation.valueextraction.ValueExtractor文件中写入上述两个提取器的全限定类名,每行一个。 - Spring Boot环境:直接将两个提取器类声明为Spring Bean,框架会自动识别并注册到校验器中。
方案2:替换Pair为自定义业务对象(语义更清晰)
不使用通用Pair存储需要校验的对象,自定义专门的数据结构,在需要校验的嵌套字段上标记@Valid即可:
// 替换Pair的专用数据结构 data class SignatureItem( @get:Valid // 标记触发内部对象的级联校验 val signatureInfo: SignatureInfo, val attachment: Any ) // 修改UploadFileReq的字段定义 data class UploadFileReq( val id: String? = null, // ...其余字段 @get:Valid val signaturesInfo: MutableList<SignatureItem> = mutableListOf() )
这种方案不需要额外扩展校验器,代码语义更明确,也能避免通用Pair泛型擦除带来的潜在问题。
补充注意:你提供的单元测试代码里
mutableListOf(signatureInfo to Object)存在编译错误,Object是类引用,这里需要传入对应类的实例,该笔误不是校验不生效的原因,修复时注意调整即可。
内容的提问来源于stack exchange,提问作者Dmitriy Fialkovskiy
相关产品推荐
相关产品推荐

