Kotlin中Hibernate Validator为何忽略自定义校验注解?
问题原因
自定义校验注解不生效是Kotlin语言特性差异加代码笔误共同导致的:
- Kotlin注解默认保留级别不支持运行时反射读取:Java定义的注解默认保留策略是
RUNTIME,但Kotlin注解默认保留级别是CLASS,即注解信息仅存在于编译期字节码,类加载到JVM后会被丢弃。Hibernate Validator是运行时通过反射扫描字段上的约束注解,自然识别不到你定义的两个自定义注解,这也是为什么同等逻辑写在Java里能正常生效。 - 校验器泛型绑定笔误:
FileFoundValidator实现ConstraintValidator接口时,第一个泛型参数错误写为Executable,导致该校验器无法和@FileFound注解绑定。 - 注解作用目标不明确:Kotlin注解默认未指定作用到字段,在类属性上标注时可能会作用到属性访问器而非实际的实例字段,同样会导致校验框架扫不到注解。
- 额外编译隐患:注解中
message字段写的${validatedValue}是Hibernate Validator识别的EL表达式,不是Kotlin字符串模板,不转义的话会触发Kotlin编译期变量找不到的错误。
修复方案
给两个自定义注解补充正确的元注解,修正校验器泛型错误、转义EL表达式的$符号即可,修复后的完整代码如下:
@Validated @ConfigurationProperties("dtn") class ExecutableAdapterConfiguration { @FileFound // <-- custom @Executable // <-- custom lateinit var executable: Path @NotEmpty lateinit var user: String @NotEmpty lateinit var password: String @NotEmpty lateinit var productId: String @NotEmpty lateinit var version: String } @Retention(AnnotationRetention.RUNTIME) @Target(AnnotationTarget.FIELD) @Constraint(validatedBy = [ExecutableValidator::class]) annotation class Executable( val message: String = "Missing permissions to execute '\${validatedValue}'", val groups: Array<KClass<*>> = [], val payload: Array<KClass<out Payload>> = [] ) class ExecutableValidator : ConstraintValidator<Executable, Path> { override fun isValid(value: Path?, context: ConstraintValidatorContext): Boolean { return value == null || !value.exists() || value.isExecutable() } } @Retention(AnnotationRetention.RUNTIME) @Target(AnnotationTarget.FIELD) @Constraint(validatedBy = [FileFoundValidator::class]) annotation class FileFound( val message: String = "File '\${validatedValue}' not found", val groups: Array<KClass<*>> = [], val payload: Array<KClass<out Payload>> = [] ) class FileFoundValidator : ConstraintValidator<FileFound, Path> { override fun isValid(value: Path?, context: ConstraintValidatorContext): Boolean { return value != null && value.exists() } }
验证说明
修改完成后重新启动应用,当application.yaml中配置的dtn.executable路径不存在、或者对应文件没有可执行权限时,Spring Boot启动阶段就会抛出校验失败异常,和@NotEmpty等内置校验注解行为完全一致。
内容的提问来源于stack exchange,提问作者Kai
相关产品推荐
相关产品推荐

