文章目录
- 一、数据校验框架简介
-
- [1、JSR(Java 规范提案):Bean Validation](#1、JSR(Java 规范提案):Bean Validation)
- 2、javax.validation.api
- 3、jakarta.validation.api
- 二、SpringBoot基础使用
一、数据校验框架简介
1、JSR(Java 规范提案):Bean Validation
JSR
:Java Specification Requests的缩写,意思是Java规范提案
- 是指向JCP(Java Community Process) 提出新增一个标准化技术规范的正式请求
- 任何人都可以提交JSR,以向Java平台增添新的API和服务
- JSR已成为Java界的一个重要标准
Bean Validation
就是这个JSR规范之一- Bean Validation是一个运行时的
数据验证框架
的标准
- Bean Validation是一个运行时的
JSR303是专家组成员向JCP提交的第1版Bean Validation,即针对bean数据校验提出的一个规范,使用注解方式实现数据校验
。后面有升级版本JSR349及JSR380。
JSR # Bean Validation官网地址:https://jcp.org/en/jsr/summary?id=bean+validation
JSR303 Bean Valiadation第一版
伴随着JAVAEE 6在2009年发布,Hibernate实现版本4.3.1.FinalJSR349 Bean Valiadation 1.1
伴随着JAVAEE 7在2013年发布,Hibernate实现版本5.1.1.FinalJSR380 Bean Valiadation 2.0
伴随着JAVAEE 8在2017年发布,完全兼容低版本的JAVA SE,Hibernate实现版本6.0.1.Final
Bean Valiadation 与 Hibernate Validation
JSR(Bean Valiadation)
规定一些校验规范即校验注解,如@Null,@NotNull,@Pattern,位于javax.validation.constraints包下,只提供规范不提供实现
- 而
Hibernate Validation
是对Bean Valiadation这个规范的实践,提供相应的实现
,并增加一些其他校验注解,如@Length,@Range等等,位于org.hibernate.validator.constraints包下
2、javax.validation.api
- Java在2009年的JAVAEE 6中发布了JSR303以及javax下的validation包内容
- JSR Bean Validation 2.0 重要版本
2.0.1.Final
xml
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
- Bean Validation 2.0中包含的注解
Hibernate-Validator
是Hibernate项目中的一个数据校验框架,是Bean Validation一种实现
- Hibernate-Validator除了提供了JSR 303规范中所有内置constraint的实现,还有一些
附加的constraint(约束)
- JSR Bean Validation 2.0对应hibernate-validator的实现重要版本
6.0.16.Final
xml
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.16.Final</version>
</dependency>
- Hibernate-Validator 扩展的注解
- spring-boot-starter-web 2.1.4.RELEASE引入的就是是这个版本
- hibernate-validator包下包含了validation-api,这个很好理解,做规范注解的实现肯定需要用到规范中定义的注解
3、jakarta.validation.api
- Java8开始,
Java EE
改名为Jakarta EE
,故javax.validation
相关的api在jakarta.validation
的包下 - JSR Bean Validation 2.0 重要版本
2.0.2
xml
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
<version>2.0.2</version>
</dependency>
- springboot后面版本不再自动导入hibernate-validator,可以导入
spring-boot-starter-validation:2.3.12.RELEASE
- 虽然依赖名改为了
jakarta
,导包还是javax
,应该是个过渡吧
二、SpringBoot基础使用
- springboot项目导入依赖,低版本不需要导入,spring-boot-starter-web就包含了
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
1、校验get请求参数
- get请求校验参数类上需要添加
@Validated
注解 - @Validated是
springmvc
提供注解,非javax.validation
的注解
java
import org.springframework.validation.annotation.Validated;
import javax.validation.constraints.Max;
import javax.validation.constraints.NotEmpty;
@RestController
@Validated
public class UserController1 {
@GetMapping("/getUser")
public String getUser(@NotEmpty(message = "name不能为空") @RequestParam("name") String name,
@Max(value = 150) @RequestParam("age") Integer age) {
return "success";
}
}
- 校验注解可以设置
message
属性(抛错提示),也可以不设置(默认中文错误提示
)
2、校验post请求参数
- post请求校验实体类上前面添加
@Valid
或@Validated
注解- 两者在这里作用相同,唯一区别是后者可以
设置分组
,后面会讲
- 两者在这里作用相同,唯一区别是后者可以
- @Valid是
javax.validation
提供注解
java
import javax.validation.Valid;
@RestController
public class UserController2 {
@PostMapping("/saveUser")
public String saveUser(@Valid @RequestBody User user) {
System.out.println(user);
return "success";
}
}
- 请求实体添加校验注解
java
import org.hibernate.validator.constraints.Range;
import org.hibernate.validator.constraints.Length;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
@Data
public class User {
// 可以为 null,若不为 null,则长度为 [5, 17]
@Length(min = 5, message = "userName长度须在[5,17]")
private String userName;
// @Size不能验证Integer,适用于String、List、Map、数组
@Size(min = 1, max = 3, message = "password长度须在[1,3]")
private String password;
// list 可以为 null,若不为 null,则长度为 [1, 5]
@Size(min = 1, max = 5, message = "list的Size在[1,5]")
private List<String> list;
@NotNull
@Valid // 级联校验,该注解将会校验自定义类
private DetailInfo detailInfo;
}
@Data
class DetailInfo {
@NotEmpty(message = "name不能为空")
private String name;
// 匹配任何数字
@Pattern(regexp = "^\\d+$", message = "itemCode需要数字")
private String itemCode;
@Range(min = 10, max = 100, message = "range在[10,100]之间")
private int age;
}
3、常用注解
注解 | 说明 | 支持类型 |
---|---|---|
@Null |
必须为null | |
@NotNull |
必须不为null | |
@NotEmpty |
必须不为null且长度大于0 | String、集合、Map、数组 |
@NotBlank |
必须不为null且去除首尾空格后长度大于0 | String |
@AssertTrue |
必须为true | boolean和Boolean |
@AssertFalse |
必须为false | boolean和Boolean |
@Min(value) |
必须是一个数字 其值必须大于或等于指定的最小值 值为null不校验 | String、Number |
@Max(value) |
必须是一个数字 其值必须小于或等于指定的最大值 值为null不校验 | String、Number |
@DecimalMin(value) |
必须是一个数字 其值必须大于或等于指定的最小值(支持小数) 值为null不校验 | String、Number |
@DecimalMax(value) |
必须是一个数字 其值必须小于或等于指定的最大值(支持小数) 值为null不校验 | String、Number |
@Size(max, min) |
元素的大小必须在指定的范围内 值为null不校验 | String、集合、Map、数组 |
@Pattern(regexp = ) |
正则表达式校验 值为null不校验 | |
@Digits(integer,fraction) |
验证整数位数和小数位数上限 值为null不校验 | String、Number |
@Past |
必须是一个过去的日期 值为null不校验 | 日期类型 |
@Future |
必须是一个将来的日期 值为null不校验 | 日期类型 |
@Email |
必须是电子邮箱地址 值为null不校验 | |
@Length(min=, max=) |
字符串长度必须在指定的范围内 值为null不校验 | String |
@Range(min=, max=) |
数字或字符串数值必须在指定范围内 值为null不校验 | String、Number |
@URI |
必须是一个有效的URL字符串 值为null不校验 | String |
注意:值为null不校验的注解一般和@NotNull一起使用
4、分组校验
- @Validated通过
分组
设置不同的校验注解 - 默认所有注解分组为
javax.validation.groups.Default
,添加Default,为了没有分组的注解生效
java
@RestController
public class UserController3 {
@PostMapping("/saveUser")
public String saveUser(@Validated(value = {User.SaveGroup.class, Default.class}) @RequestBody User user) {
System.out.println(user);
return "success";
}
@PostMapping("/updateUser")
public String updateUser(@Validated(value = {User.UpdateGroup.class, Default.class}) @RequestBody User user) {
System.out.println(user);
return "success";
}
}
java
@Data
public class User {
// 默认就是Default分组
public interface SaveGroup {
}
public interface UpdateGroup {
}
// 新增时必须为空
@Null(groups = SaveGroup.class)
// 修改是必须不为空
@NotNull(groups = UpdateGroup.class)
private Long id;
@NotEmpty
private String userName;
}
5、自定义校验规则
- 对于常用的注解的验证规则内容在
xxxValidator
中,如下
- 自定义状态注解,validatedBy则是实现类的Class对象
java
@Documented
@Constraint(validatedBy = {StatusValidator.class})
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
public @interface Status {
String message() default "状态标识只能为0或1";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
- 注解实现类,ConstraintValidator的第一个泛型为
自定义注解
,第二个为支持类型
java
public class StatusValidator implements ConstraintValidator<Status, String> {
@Override
public boolean isValid(String bool, ConstraintValidatorContext constraintValidatorContext) {
// 模仿一般的注解,值为null不做校验,和@NotEmpty一起使用
if (bool == null) {
return true;
}
// 包含在0和1里面返回true通过,不包含则不通过
return Arrays.asList("0","1").contains(bool);
}
}
5、校验模式
默认
普通模式- 校验完所有的属性,然后返回所有的验证失败信息
快速失败
返回模式- 只要有一个属性校验失败就立马返回
开启快速失败返回模式
java
@Configuration
public class HibernateValidatorConfiguration {
@Bean
public Validator validator(){
ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
.configure()
// true 快速失败返回模式 false 普通模式
.addProperty( "hibernate.validator.fail_fast", "true" )
.buildValidatorFactory();
Validator validator = validatorFactory.getValidator();
return validator;
}
}
6、全局异常处理
- get和post参数校验抛出异常类型不一样
java
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
@ResponseBody
public ResultEntity<String> handleBindException(Exception ex) {
// post请求参数校验
if (ex instanceof MethodArgumentNotValidException) {
MethodArgumentNotValidException exs = (MethodArgumentNotValidException) ex;
FieldError fieldError = exs.getBindingResult().getFieldError();
return ResultEntity.fail(400, fieldError.getDefaultMessage(), null);
}
// get请求参数校验
if (ex instanceof ConstraintViolationException) {
ConstraintViolationException exs = (ConstraintViolationException) ex;
Set<ConstraintViolation<?>> violations = exs.getConstraintViolations();
for (ConstraintViolation<?> item : violations) {
return ResultEntity.fail(400, item.getMessage(), null);
}
}
return ResultEntity.fail(500, ex.getMessage(), null);
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class ResultEntity<T> {
private Integer code;
private String message;
private T data;
public static <T> ResultEntity<T> fail(Integer code, String msg, T t) {
return new ResultEntity<T>(code, msg, t);
}
}