0%

SpringBoot JSR303 分组校验和自定义校验注解

环境准备:引入 spring-boot-starter-web 启动器依赖

1. 基础校验

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
@Data
@TableName("pms_brand")
public class BrandEntity implements Serializable {
private static final long serialVersionUID = 1L;

/**
* 品牌id
*/
@TableId
private Long brandId;
/**
* 品牌名
*/
@NotBlank(message = "品牌名不能为空")
private String name;
/**
* 品牌logo地址
*/
@NotBlank(message = "品牌logo地址不能为空")
@URL(message = "品牌logo地址必须是一个合法的url地址")
private String logo;
/**
* 介绍
*/
@NotBlank(message = "品牌介绍不能为空")
private String descript;
/**
* 显示状态[0-不显示;1-显示]
*/
@NotNull(message = "品牌状态不能为空")
private Integer showStatus;
/**
* 检索首字母
*/
@NotBlank(message = "检索首字母不能为空")
@Pattern(regexp = "^[a-zA-Z]$", message = "检索首字母必须在a-z或者A-Z范围内")
private String firstLetter;
/**
* 排序
*/
@NotNull
private Integer sort;

}

如上使用 @NotBlank、@URL、@Pattern 等校验注解对实体类对应字段进行校验,要开启校验注解,还需要在映射方法入参前加上注解 @Valid,例如下述代码:

1
2
3
4
5
@RequestMapping("/save")
public R save(@Valid @RequestBody BrandEntity brand) {
brandService.save(brand);
return R.ok();
}

2. 分组校验

在一下特殊场合,可能需要进行分组校验,观察 BrandEntity 类 brandId 字段,当我们调用新增方法时,brandId 需要为空。在进行更新方法时,brandId 不能为空。为了实现这种分组校验效果,我们可以使用注解中的 groups 参数来实现。

image-20210614102006173

2.1 实现分组接口

创建一个 AddGroup 和 UpdateGroup 接口,接口内不需要任何方法和属性,接口仅作为唯一标识使用。

2.2 声明分组

如下我们在 brandId 字段上使用多个分组,如下代码所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Data
@TableName("pms_brand")
public class BrandEntity implements Serializable {
private static final long serialVersionUID = 1L;

/**
* 品牌id
*/
@NotNull(message = "更新品牌时品牌id不能为空", groups = {UpdateGroup.class})
@Null(message = "新增品牌时品牌id必须为空", groups = {AddGroup.class})
@TableId
private Long brandId;

//....
}

注意:开启分组后,原有没有指定分组的注解会失效,所以需要对使用校验注解的字段开启分组

2.3 开启分组

这里不再使用 @Valid 注解,而是使用 @Validated 注解,该注解支持指定校验分组,如下代码所示:

1
2
3
4
5
6
7
8
9
10
/**
* 保存
* @param brand 品牌实体
* @return
*/
@RequestMapping("/save")
public R save(@Validated(value = AddGroup.class) @RequestBody BrandEntity brand) {
brandService.save(brand);
return R.ok();
}

3. 自定义校验注解

@Pattern 注解只适用于字符串,目前 BrandEntity 中的 showStatus 字段仅接收 0 和 1 的整数,我们可以实现自定义校验注解来实现这种效果。

image-20210614105020055

3.1 创建注解

其中 message 、groups 和 payload 为校验注解的三个基本方法,不可以缺少。values 方法则为自定义方法,表示传入的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* @author shotozheng
*/
@Documented
@Constraint(validatedBy = { ListValueConstraintValidator.class }) // 指定自定义的校验器
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE }) // 注解支持使用的位置
@Retention(RUNTIME) // 注解运行时获取
public @interface ListValue {

/** 默认提示信息 */
String message() default "{com.atguigu.common.validate.ListValue.message}";

/** 分组 */
Class<?>[] groups() default { };

Class<? extends Payload>[] payload() default { };

int[] values() default { };
}

message 方法用于从默认名为 ValidationMessage.properties 的配置文件中读取自定义的提示信息,因此需要在 resources 文件夹下创建 ValidationMessage.properties 配置文件,内容如下:

1
2
## 自定义校验注解默认消息提示配置
com.atguigu.common.validate.ListValue.message=不合法的参数值

3.2 创建注解校验器

@Constraint 注解用于指定自定义校验器,@Constraint 注解内容如下所示:

image-20210614112926344

其中 validateBy 方法指明传入的参数必须是 ConstraintValidator 接口的实现类,同时需要指明泛型,查看如下注释信息。第一个泛型表示自定义注解 ListValue 类型,第二个则是注解使用在的目标字段类型,由于我们使用自定义注解是作用在 BrandEntity 中的 Integer 类型的 showStatus 的字段上,因此第二个泛型为 Integer。

image-20210614113115704

下面我们创建自定义注解校验器,代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
* 自定义注解 @ListValue校验器
*/
public class ListValueConstraintValidator implements ConstraintValidator<ListValue, Integer> {

private Set<Integer> dataSet = new HashSet<>();

/**
* 初始化方法
* values 数组的值为注解上指明的值,即0和1
* @param constraintAnnotation
*/
@Override
public void initialize(ListValue constraintAnnotation) {
int[] values = constraintAnnotation.values();
if (values != null) {
for (int val : values) {
dataSet.add(val);
}
}
}

/**
* 校验方法
* @param value 需要校验的值
* @param context
* @return
*/
@Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
return dataSet.contains(value);
}
}

然后需要在 @ListValue 注解中指明使用的校验器即可,如:@Constraint(validatedBy = { ListValueConstraintValidator.class })

3.3 使用自定义注解

最后在 BrandEntity 的 showStatus 字段上使用 @ListValue 注解即可实现相应校验功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Data
@TableName("pms_brand")
public class BrandEntity implements Serializable {

// ....
/**
* 显示状态[0-不显示;1-显示]
*/
@NotNull(message = "品牌状态不能为空", groups = {AddGroup.class, UpdateGroup.class, UpdateStatusGroup.class})
@ListValue(values = {0, 1}, groups = {AddGroup.class, UpdateGroup.class, UpdateStatusGroup.class})
private Integer showStatus;

// ....
}
------ 本文结束------