Spring中自定义ConstraintValidator未触发问题排查
看起来你的自定义校验器没生效主要有几个关键问题,咱们一步步来排查修复:
1. 核心错误:字段名获取方式完全错误
你的DetailsFieldValidatorImpl里,初始化时用了it.toString().toUpperCase()来获取POJO的字段名,但Kotlin的KProperty.toString()返回的是完整的属性签名(比如val DetailsDocument.fullName: kotlin.String),而不是单纯的字段名!这导致你的valueList里存的都是类似VAL DETAILSDOCUMENT.FULLNAME: KOTLIN.STRING的字符串,和传入的maritalStatus转大写后的MARITALSTATUS完全不匹配,自然永远不会触发校验失败。
修复方法:改用KProperty.name来获取真实的字段名:
override fun initialize(constraintAnnotation: DetailsFieldValidator) { valueList = constraintAnnotation.sourceClass.declaredMemberProperties .map { it.name.toUpperCase() } // 这里用it.name而不是it.toString() }
2. 确保字段映射正确
你的示例JSON里的字段是detail,但Account类里对应的字段是name——Spring MVC默认是按字段名匹配JSON键的,所以如果不做映射,JSON里的detail值根本不会绑定到Account.name字段上,校验器自然拿不到值,也就不会执行校验。
修复方法:要么把Account的字段名改成detail,要么添加@JsonProperty注解指定映射:
data class Account( @JsonProperty("detail") // 绑定JSON的detail字段到这个属性 @DetailsFieldValidator(sourceClass = DetailsDocument::class) val name: String, val values: List<String>, val active: Boolean )
3. 必须触发Spring的校验机制
Spring不会自动对所有@RequestBody的对象执行校验,你需要在Controller方法的参数上添加@Valid或@Validated注解来触发校验流程:
@PostMapping("/your-endpoint") fun handleRequest(@Valid @RequestBody account: Account): ResponseEntity<*> { // 业务逻辑 }
如果用@Validated,还可以指定校验组,但基础场景用@Valid就够了。
4. 确认依赖配置正确
Spring Boot 2.3.0.RELEASE的spring-boot-starter-validation确实包含了Hibernate Validator(校验的核心实现),你已经添加的依赖是对的,但可以确认一下是否有依赖冲突——比如如果你的项目里引入了旧版的hibernate-validator,可能会导致问题。可以执行./gradlew dependencies(Gradle)或mvn dependency:tree(Maven)来排查冲突。
额外验证小技巧
可以在isValid方法里加个日志打印,确认校验器是否被调用、传入的value是什么、valueList的内容是什么,这样能快速定位问题:
import org.slf4j.LoggerFactory class DetailsFieldValidatorImpl : ConstraintValidator<DetailsFieldValidator, String> { private val logger = LoggerFactory.getLogger(javaClass) private var valueList: List<String>? = null override fun isValid(value: String?, context: ConstraintValidatorContext?): Boolean { logger.info("校验值:{},允许的字段列表:{}", value, valueList) return valueList?.contains(value?.toUpperCase()) ?: false } // ... initialize方法修改后的代码 }
按上面的步骤修复后,你的校验器应该就能正常触发,拦截无效的detail字段值了。
内容的提问来源于stack exchange,提问作者MeanwhileInHell

