目录
开始
未来实现效果
第一步:编写自定义校验注解
第二步:编写自定义校验器
第三步:编写配置文件
效果演示
开始
未来实现效果
编写一个 @ListValue 注解,可以实现功能有:
- 限定字段的值,例如指定只能为 1 或 2.
- 支持自定义错误信息.
- 支持分组校验.
第一步:编写自定义校验注解
import jakarta.validation.Constraint
import jakarta.validation.Payload
import kotlin.annotation.AnnotationRetention.RUNTIME
import kotlin.annotation.AnnotationTarget.*
import kotlin.reflect.KClass
@MustBeDocumented
@Constraint(validatedBy = [ListValueConstraintValidator::class])
@Target(FUNCTION, FIELD, ANNOTATION_CLASS, CONSTRUCTOR, VALUE_PARAMETER, TYPE)
@Retention(RUNTIME)
annotation class ListValue(
val message: String = "{org.cyk.gulimall.product.infra.config.exception.ListValue.message}",
val groups: Array<KClass<*>> = [],
val payload: Array<KClass<out Payload>> = [],
val vals: IntArray = []
)
a)注解解释:
-
@MustBeDocumented
这个注解表示使用该注解的注解应该包含在生成的Kotlin文档中。换句话说,当使用KDoc生成文档时,应用了 MustBeDocumented 的注解将会出现在文档中。
-
@Constraint(validatedBy = [ListValueConstraintValidator::class])
这个注解定义了该注解是一个校验约束,并且指定了用来校验该注解的逻辑类。ListValueConstraintValidator::class 是一个自定义校验类(下文中详细讲)
-
@Target
这个注解定义了自定义注解可以应用的目标元素。可以是方法、字段、注解类型、构造函数、参数或类型使用。在Kotlin中,它们分别对应 FUNCTION, FIELD, ANNOTATION_CLASS, CONSTRUCTOR, VALUE_PARAMETER, TYPE 这样可以控制自定义注解可以应用于哪些代码元素。
-
@Retention(RUNTIME)
这个注解定义了自定义注解的保留策略. RUNTIME表示注解将在运行时保留,因此可以通过反射机制访问注解信息。其他可选的保留策略包括 SOURCE(只在源代码中保留)和 CLASS(在编译时保留,但在运行时不可见)。
b)成员解释:
Note:message、groups、payload 在自定义校验注解中必须要有,因为是 JSR303 中的规范(可以参考其他校验注解,例如 @NotBlank)
- message:自定义错误信息去哪个地方取(下文中会详细讲解).
- groups:支持分组校验功能.
- payload:支持自定义负载信息.
- vals:vals 是自定义的,用来给校验注解传递值,例如 @ListValue(vals = [1, 2]).
第二步:编写自定义校验器
上面提到的 @Constraint(validatedBy = [ListValueConstraintValidator::class]) 中的 ListValueConstraintValidator 就是自定义校验器,负责处理校验核心逻辑.
例如你是这样使用注解的@ListValue(vals=[1,2])
那么 initialize 方法就可以拿到其中 vals,进行初始化(逻辑自定义)
isValid 方法就是将来使用时触发的逻辑
import jakarta.validation.ConstraintValidator
import jakarta.validation.ConstraintValidatorContext
class ListValueConstraintValidator: ConstraintValidator<ListValue, Int> {
private lateinit var set: Set<Int>
/**
* 初始化方法
*/
override fun initialize(constraintAnnotation: ListValue) {
set = constraintAnnotation.vals.toSet()
}
/**
* @param p0: 需要校验的值
*/
override fun isValid(p0: Int, p1: ConstraintValidatorContext): Boolean {
return set.contains(p0)
}
}
Ps:@Constraint(validatedBy = [ ... ]) 中还可以指定多个不同的校验器,来适配不同类型的校验
第三步:编写配置文件
在编写自定义校验注解时,里面有一个成员 message,他就会去配置文件(ValidationMessages.properties)找这个配置.
因此这里我们在 resource 目录下需要创建一个 ValidationMessages.properties 配置文件,编写内容如下:
org.cyk.gulimall.product.infra.config.exception.ListValue.message=must be a specified value~
Ps:key 就是 message 默认值(一般就取 自定义校验注解的全限定类名.message ),value 就是校验错误提示的默认信息
效果演示
a)demo 如下:
@GetMapping("/value")
fun getValue(
@RequestBody @Valid dto: ValueDto
): ApiResp<String> {
return ApiResp.ok("ok")
}
data class ValueDto (
@field:ListValue(vals = [1, 2])
val status: Int
)
a)当输入错误的值时.
Ps:这个默认错误信息,也可以在统一异常处理中,专门提取出来.
b)输入的值正确时