通常我们在开发的过程中,需要对前端传入的数据进行校验,尽管这一步已经在前端进行了一次校验,虽然现在已经有了很多校验的注解,@NotNull、@NotBlank、@URL等一系列注解帮助我们进行校验,但是在实际的业务开发过程中,这些可能不足以满足我们的需求,这时候我们就需要自己来定义注解了。
可能需要用到的依赖按需导入:
org.hibernate
hibernate-validator
5.4.1.Final
org.jboss.logging
jboss-logging
3.3.0.Final
com.fasterxml
classmate
1.3.3
@Constraint
这个注解的主要作用就是帮助我们来处理验证逻辑的,根据根据自己的业务需求来完成这一块验证的逻辑。下面我们就来简单测试一下。
**场景 **:假设我们一个实体类的属性如下解释,我们需要对前端传入的数字是不是0或1。
/**
* 显示状态[0-不显示;1-显示]
*/
private Integer showStatus;
定义注解
@Documented
@Constraint(validatedBy = {ListValueConstraintValidator.class})
@Target({ElementType.METHOD,ElementType.FIELD,ElementType.ANNOTATION_TYPE,ElementType.CONSTRUCTOR,ElementType.PARAMETER,ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ListValue {
String message() default "{com.atguigu.common.valid.ListValue.message}";
Class[] groups() default {};
int[] vals() default {};
Class[] payload() default {};
}
message():错误信息,如果我们没有在注解中定义错误信息的话,他会默认去寻找com.atguigu.common.valid.ListValue.message为key的错误信息 groups():这个主要是来进行分组验证的。 ** vals() ** :自行定义的值
我们可以看到@Constraint中 @Constraint(validatedBy = {ListValueConstraintValidator.class}) 这段意思是我们将此注解的验证逻辑交给ListValueConstraintValidator来处理,这里是一个数组,我们可以传入多个处理逻辑。
ConstraintValidator接口,它有两个泛型,第一个是自定义的注解类,第二个就是要验证的数据的类型,这两个类里面都有两个方法,initialize和isValid,第一个是初始化方法,第二个是验证的逻辑方法,返回true,则验证通过,否则则不通过。
ListValueConstraintValidator自定义验证逻辑public class ListValueConstraintValidator implements ConstraintValidator {
private Set set = new HashSet();
/**
* 初始化 加载注解的信息。也就是带有此注解Bean上的vals值
* @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) {
return set.contains(value);
}
}
** ValidationMessages.properties **
规范(JSR303)说,我必须将ValidationMessages.properties放到我的类路径根目录中,我希望它应该是MyDeploymentUnit.war / WEB-INF / classes / ValidationMessages.properties,这就是我的应用程序的构建和部署方式。出于调试原因,我将此代码添加到了自定义验证器中,以确保文件存在并正确设置。
key就是我们在注解定义的默认 message的值。
在Controller中需要进行校验的Bean加上 ** @Validated ** 注解
需要验证的 属性 这里的如果校验不通过的话,他会抛出一个异常,我们只需要捕获到这个错误信息即可。
如果我们去掉属性声明的message信息 他会去寻找默认的ValidationMessages.properties下的信息。
可能大家会注意到我在Controller中使用 @Validated 注解时会带有一个value值,他的作用是对操作进行分组。比如请看如下
@NotNull(message = "修改商品时请传入商品id",groups = {UpdateGroup.class})
@Null(groups = {AddGroup.class},message = "不能指定id")
@TableId
private Long brandId;
商品的数据表的对应的ID,我们可能在进行新增操作时无需我们来指定对应的ID,因为我们一般设置都会默认自增的。而我们在查询或者更新的时候可能会根据ID来查出这一条记录。总的来说的就是在进行更新时需要ID,新增不需要。每一个注解都会带有groups这个属性来声明自己的组。
如何使用呢? 很简单:
定义对应的接口或者类。
public interface AddGroup {
}
// ------------------------------
public interface UpdateGroup {
}
//---------------------------------
public interface UpdateStatusGroup {
}
我们只需要定义三个接口来声明组,无需任何操作。
在Controller中的@Validated 注解中声明对那个组进行验证。
@RequestMapping("/save")
public R save(@Validated(value = {AddGroup.class}) @RequestBody BrandEntity brand) {
brandService.save(brand);
return R.ok();
}
实体类进行分组逻辑
@NotNull(message = "修改商品时请传入商品id",groups = {UpdateGroup.class})
@Null(groups = {AddGroup.class},message = "不能指定id")
@TableId
private Long brandId;
验证: 新增时:传入ID会失败
更新时:不传ID 会失败