https://www.bilibili.com/video/BV17i4y157Ah?spm_id_from=333.999.0.0&vd_source=763ee0e99f0ab481ec743313a7f80d92(侵权告知删除)
常用注解@Null(groups={Add.class}) 参数必须为null,group设置分组,默认为default
@NotNull 参数不为null
@NotEmpty 参数不为null ,"",集合不为空
@NotBlank 参数不为null, "", " ",只能作用字符串类型
@AssertFalse 被注释的元素必须是false
@AssertTrue 被注释的元素必须是true
@Min(value) 被注释的元素必须为一个数字 >=
@Max(value) 被注释的元素必须为一个数字 <=
@DecimalMin("value") >=
@DecimalMax("value") <=
@NegativeOrZero <=0
@Range(min,max) 被注释的元素大小必须在指定的范围内
@Size(min ,max) 被注释的元素大小必须在指定的范围内
@Email 被注释的元素必须是电子邮箱地址
@Past 被注释的元素必须是一个过去的日期
@PastOrPresent 被注释的元素必须是一个过去的时间
@Future 被注释的元素必须是一个将来的日期
@Pattern(regexp = "1(3[0-9]|4[01456879]|5[0-35-9]|6[2567]|7[0-8]|8[0-9]|9[0-35-9])\d{8}$") 被注释的元素必须是符合指定的正则表达式
@URL 被注释的元素必须是链接地址
备注:参数校验只有!= null的时候才生效
1.导入依赖
org.hibernate.validator
hibernate-validator
6.0.18.Final
org.apache.tomcat.embed
tomcat-embed-el
9.0.29
2.测试
1.定义实体类
@Data
public class UserInfo{
@NotBlank
private String name;
}
2.编写工具类
public class UserInfo{
//声明validator,线程安全的:所有的方法都可以使用这个对象,而不会产生线程安全的问题
private static Validator validator;
//初始化默认的validator对象
static {validator = Validation.buildDefaultValidatorFactory().getValidator();}
//校验
public static List valid(UserInfo userInfo,Class>... groups){
//如果被校验对象userInfo没有检验通过,则set里面就有校验信息,返回出去
Set> set = validator.validate(userInfo,groups);
List list = set.stream().map(v -> "属性:" + v.getPropertyPath() + ",属性值是:" + v.getInvalidValue()
+ ",校验不通过的提示信息:" + v.getMessage()+",消息模板:"+v.getMessageTemplate()).collect(Collectors.toList());
return list;
}
}
3.测试
public class ValidationTest {
public static void main(String[] args) {
UserInfo info = new UserInfo();
info.setName("名字");
}
List valid1 = ValidationUtil.valid(info);
System.out.println(valid1);
返回结果:
[属性:name,属性值是:null,校验不通过的提示信息:不能为空,消息模板:{javax.validation.constraints.NotBlank.message}]
3.自定义消息模板
@NotBlank(message = "姓名不能为空!!") private String name; 返回结果: [属性:name,属性值是:null,校验不通过的提示信息:姓名不能为空!!,消息模板:姓名不能为空!!]4.分组校验
1.定义实体类
public class UserInfo{
//标记接口 新增,修改,删除
public interface Add extends Default {}
public interface Update extends Default{}
public interface Delete extends Default{}
//默认组default,指定分组
@Null(groups = {Add.class}) //只适用于新增
@NotNull(groups = {Update.class,Delete.class})//只适用于修改/删除
private Long id;
@NotBlank(message = "姓名不能为空!!")
private String name;}
2.编写工具类
//校验,要检验的对象,检验分组
public static List valid(UserInfo userInfo,Class>... groups){
//如果被校验对象userInfo没有检验通过,则2set里面就有校验信息,返回出去
Set> set = validator.validate(userInfo,groups);
List list = set.stream().map(v -> "属性:" + v.getPropertyPath() + ",属性值是:" + v.getInvalidValue()
+ ",校验不通过的提示信息:" + v.getMessage()+",消息模板:"+v.getMessageTemplate()).collect(Collectors.toList());
return list;
}
3.测试
public class ValidationTest {
public static void main(String[] args) {
UserInfo info = new UserInfo();
info.setName("名字");
}
List valid1 = ValidationUtil.valid(info,UserInfo.Add.class);
List valid2 = ValidationUtil.valid(info,UserInfo.Update.class);
System.out.println(valid1);
System.out.println(valid2);
//结果
valid1[]
valid1[属性:id,属性值是:null,校验不通过的提示信息:不能为null,消息模板:{javax.validation.constraints.NotNull.message}]
4.级联校验
1.编写实体类
public class UserInfo{
@NotBlank(message = "姓名不能为空!!")
private String name;
@Valid //被引用对象加上@valid注解,才可以完成级联校验
private Grade grade;
}
public class Grade {
@NotBlank
private String id;
}
2.工具类同上
3.测试
public class ValidationTest {
public static void main(String[] args) {
UserInfo info = new UserInfo();
info.setName("名字");
//添加grade
Grade grade = new Grade();
//grade.setId("123");
info.setGrade(grade);
}
//结果
valid1[属性:grade.id,属性值是:null,校验不通过的提示信息:不能为空,消息模板:{javax.validation.constraints.NotBlank.message}]
5.自定义注解
status的必须为1000/1001/1002
1.编写实体类
public class UserInfo{
@NotBlank(message = "姓名不能为空!!")
private String name;
@UserStatus
private Integer status;
}
2.1编写注解要校验的规则
-继承ConstraintValidator,绑定要校验的约束注解UserStatus,类型
public class UserStatusValidator implements ConstraintValidator {
@Override
public void initialize(UserStatus constraintAnnotation) {
}
@Override
public boolean isValid(Integer value, ConstraintValidatorContext constraintValidatorContext) {
if (value == null){
//没有值,不校验
return true;
}
//设置要求之
Set set = new HashSet<>();
set.add(1000);
set.add(1001);
set.add(1002);
return set.contains(value);
}
}
2.2编写注解声明类
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {UserStatusValidator.class})//说明当前注解要被谁来完成校验工作
@Documented
public @interface UserStatus {
String message() default "{userStatus必须是1000/1001/1002}";
Class>[] groups() default {};
Class extends Payload>[] payload() default {};
}
3.测试
public class ValidationTest {
public static void main(String[] args) {
UserInfo info = new UserInfo();
info.setName("名字");
info.setStatus(200);
List valid1 = ValidationUtil.valid(info);
System.out.println("valid1"+valid1);
}
//结果
valid1[属性:status,属性值是:200,校验不通过的提示信息:{userStatus必须是1000/1001/1002},消息模板:{userStatus必须是1000/1001/1002}, 属性:grade,属性值是:null,校验不通过的提示信息:不能为空,消息模板:{org.hibernate.validator.constraints.NotEmpty.message}]
6.快速校验
1.实体类 同上
2.工具类
public class ValidationUtil {
//配置快速失败
private static Validator validFailFast;
static {
validFailFast = Validation.byProvider(HibernateValidator.class).configure()
.failFast(true).buildValidatorFactory().getValidator();//.failFast(true),快速失败
}
public static List validNotBean(T object, Method method, Object[] parameterValues, Class>... groups){
Set> set = executables.validateParameters(object, method, parameterValues, groups);
List list = set.stream().map(v -> "属性:" + v.getPropertyPath() + ",属性值是:" + v.getInvalidValue()
+ ",校验不通过的提示信息:" + v.getMessage()+",消息模板:"+v.getMessageTemplate()).collect(Collectors.toList());
return list;
}
3.测试类同上
调用: List valid = ValidationUtil.validFailFast(info,UserInfo.Add.class);
效果:当有错误时,直接返回第一个错误,不再校验其他错误。
7.非bean入参校验
请求参数和返回值并不是一个bean
1.编写要校验的方法
public String getByName(@NotBlank String name){
//执行入参校验
//当前线程的堆栈,第一个元素就是当前所在方法的名字
StackTraceElement stackTraceElement = Thread.currentThread().getStackTrace()[1];
String methodName = stackTraceElement.getMethodName();
Method method = null;
try{
method = this.getClass().getDeclaredMethod(methodName, String.class);
}catch (Exception e) {
e.printStackTrace();
}
List strings = ValidationUtil.validNotBean(this, method, new Object[]{name});
//打印校验结果
System.out.println("校验结果:" + strings);
return "ok";
}
2.工具类编写
public class ValidationUtil {
private static ExecutableValidator executables;
static {
validator = Validation.buildDefaultValidatorFactory().getValidator();
//校验入参或返回值的
executables = validator.forExecutables();
}
public static List validNotBean(T object, Method method, Object[] parameterValues, Class>... groups){
//校验方法参数 validateParameters
Set> set = executables.validateParameters(object, method, parameterValues, groups);
List list = set.stream().map(v -> "属性:" + v.getPropertyPath() + ",属性值是:" + v.getInvalidValue()
+ ",校验不通过的提示信息:" + v.getMessage()+",消息模板:"+v.getMessageTemplate()).collect(Collectors.toList());
return list;
}
}
3.测试
method.getByName("");
返回结果
校验结果:[属性:getByName.arg0,属性值是:,校验不通过的提示信息:不能为空,消息模板:{javax.validation.constraints.NotBlank.message}]
8.与SpringBoot整合
1.编程式校验
直接使用工具类校验
@GetMapping("/addUser")
public String getByName(UserInfo userInfo){
List valid = ValidationUtil.valid(userInfo);
if (valid.size()>0){
System.out.println(valid);
return "校验不成功";
}else {
return "添加成功!";}
}
访问:http:8080/addUser
结果:校验不成功 控制台打印报错
2.声明式校验
@GetMapping("/addUser2")
public String getByName2(@Valid UserInfo userInfo){
return "添加成功";}
结果:页面直接报错
将校验结果封装到BindingResult,不会页面上报错
@GetMapping("/addUser2")
public String getByName2(@Valid UserInfo userInfo, BindingResult result){
if(result.hasErrors()){//判断是否有不满足约束的
List errors = result.getAllErrors();
for (ObjectError error : errors) {
//打印错误信息
System.out.println(error.getObjectName()+"::"+error.getDefaultMessage());
}
//获取没通过校验的字段详情
List fieldErrorList = result.getFieldErrors();
for (FieldError fieldError : fieldErrorList) {
System.out.println(fieldError.getField() + "::" + fieldError.getDefaultMessage() + ",当前没有通过校验规则的值是:" + fieldError.getRejectedValue());
}
};
return "添加成功";
}
3.@Validated 实现分组
@GetMapping("/addUser3")
public String getByName3(@Validated(value = {UserInfo.Add.class}) UserInfo userInfo, BindingResult result){
if(result.hasErrors()){//判断是否有不满足约束的
List errors = result.getAllErrors();
for (ObjectError error : errors) {
System.out.println(error.getObjectName()+"::"+error.getDefaultMessage());
}
//获取没通过校验的字段详情
List fieldErrorList = result.getFieldErrors();
for (FieldError fieldError : fieldErrorList) {
System.out.println(fieldError.getField() + "::" + fieldError.getDefaultMessage() + ",当前没有通过校验规则的值是:" + fieldError.getRejectedValue());
}
};
return "添加成功";
}
9.统一异常处理
方式一:添加一个方法,使用@ExceptionHandler(BindException.class)标记,处理当前controller里抛出的xx异常
@ExceptionHandler(BindException.class)
public String handleEx(BindException e){
List fieldErrors = e.getFieldErrors();
StringBuilder builder = new StringBuilder();
for (FieldError fieldError : fieldErrors) {
builder.append("属性:").append(fieldError.getField()).append("校验不通过的原因:").append(fieldError.getDefaultMessage()).append(";;");
}
return builder.toString();
}
方式二:编写统一的异常处理方式
@ControllerAdvice
public class RoadControllerAdvice {
@ExceptionHandler(BindException.class)
@ResponseBody
public String handleEx(BindException e){
List fieldErrors = e.getFieldErrors();
StringBuilder builder = new StringBuilder("RoadControllerAdvice里的:");
for (FieldError fieldError : fieldErrors) {
builder.append("属性:").append(fieldError.getField()).append("校验不通过的原因:").append(fieldError.getDefaultMessage()).append(";;");
}
return builder.toString();
}
@ExceptionHandler(ConstraintViolationException.class)
@ResponseBody
public List handleEx(ConstraintViolationException e){
Set> fieldErrors = e.getConstraintViolations();
StringBuilder builder = new StringBuilder("RoadControllerAdvice里的:");
List list = fieldErrors.stream().map(v -> "属性:" + v.getPropertyPath() + ",属性值是:" + v.getInvalidValue()
+ ",校验不通过的提示信息:" + v.getMessage()+",消息模板:"+v.getMessageTemplate()).collect(Collectors.toList());
return list;
}
@ExceptionHandler(Exception.class)
@ResponseBody
public String handleEx(Exception e){
return e.getMessage();
}
}



