文章目录
上一节讲解了如何使用分组校验。
这一节将详细介绍如何在Java中实现自定义校验注解以及配套的校验器。
1. 需求介绍
假设我们需要校验一个名为 showStatus
的字段,该字段只能接受 0
或 1
两个值。
为了实现这一需求,我们将创建一个自定义校验注解,并为其编写相应的校验器。
2. 创建自定义校验注解
创建自定义注解的步骤:
- ① 创建注解类
- ② 创建提示信息配置文件
- ③ 创建校验器
- ④ 管理注解类和校验器
2.1 编写自定义校验注解
2.1.1 注解定义
首先,我们需要定义一个自定义的校验注解 @ListValue
,并为其提供必要的元数据。
ListValue
注解类的代码如下。
package com.atguigu.common.validator.validate;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Documented
@Constraint(validatedBy = { ListValueConstraintValidator.class })
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface ListValue {
String message() default "{com.xunqi.common.valid.ListValue.message}";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
int[] vals() default { };
}
这段代码定义了一个自定义的校验注解 @ListValue
,用于在Java应用程序中对特定的值进行校验。下面是这个注解的核心部分的解释:
-
@Constraint(validatedBy = { ListValueConstraintValidator.class })
:- 这个元注解表明
@ListValue
是一个校验注解,并指定了一个校验器ListValueConstraintValidator
。这个校验器负责具体的校验逻辑。
- 这个元注解表明
-
public @interface ListValue
:- 定义了一个名为
ListValue
的注解接口。
- 定义了一个名为
-
String message() default "{com.xunqi.common.valid.ListValue.message}"
:message
属性定义了当校验失败时的默认错误消息。这里的默认值是一个占位符,表示错误消息将从一个配置文件中读取,通常是在ValidationMessages.properties
文件中。
-
Class<?>[] groups() default {}
:groups
属性支持分组校验,允许用户定义不同的校验组,并且可以基于这些组选择性地执行校验。
-
int[] vals() default {}
:vals
属性定义了允许的值列表。这是@ListValue
注解特有的属性,用于指定哪些整数值是合法的。
@ListValue
注解被设计用来校验一个字段或参数的值是否在一个预定义的整数列表中。
使用这个注解将调用 ListValueConstraintValidator
来验证值是否有效。
如果校验失败,则会抛出一个包含错误消息的异常,错误消息从配置文件中获取。
2.1.2 配置文件
我们还需要创建一个配置文件来定义错误消息。
# ValidationMessages.properties
com.example.validation.ListValue.message=必须提交指定的值
3. 实现自定义校验器
3.1 编写自定义校验器
为了执行具体的校验逻辑,我们需要实现 ConstraintValidator
接口。
package com.atguigu.common.validator.validate;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.HashSet;
import java.util.Set;
public class ListValueConstraintValidator implements ConstraintValidator<ListValue,Integer> {
private Set<Integer> set = new HashSet<>();
/**
* 初始化方法
* @param constraintAnnotation
*/
@Override
public void initialize(ListValue constraintAnnotation) {
int[] vals = constraintAnnotation.vals();
for (int val : vals) {
set.add(val);
}
}
/**
* 判断是否效验成功
* @param value 需要效验的值
* @param context
* @return
*/
@Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
//判断是否有包含的值
boolean contains = set.contains(value);
return contains;
}
}
initialize
: 在校验开始前初始化校验器,获取允许的值列表。isValid
: 校验逻辑,检查给定值是否在允许的值列表中。
4. 使用自定义校验
在实体类中,我们可以使用 @ListValue
注解标记需要校验的字段。
public class BrandEntity {
// ...
@ListValue(values = {0, 1})
private Integer showStatus;
// ...
}
5. 多类型校验器的支持
如果 showStatus
字段的类型改变,例如变为 Double
,我们可能需要额外的校验器来处理这种类型。在这种情况下,我们可以通过在 @ListValue
注解中指定多个校验器来解决这个问题。
@Constraint(validatedBy = {ListValueValidatorForInteger.class, ListValueValidatorForDouble.class})
public @interface ListValue {
// ...
}
6. 测试
最后,我们需要确保前后端都实现了数据校验,并且能够正确处理各种情况,如分组校验和自定义校验。
- 前端校验:确保前端能够识别错误并给出提示。
- 后端校验:确保后端能够正确识别和处理不符合要求的数据。