1、什么是JSR-303
JSR-303 是 JAVA EE 6 中的一项子规范,叫做 Bean Validation。
在任何时候,当你要处理一个应用程序的业务逻辑,数据校验是你必须要考虑和面对的事情。应用程序必须通过某种手段来确保输入进来的数据从语义上来讲是正确的。在通常的情况下,应用程序是分层的,不同的层由不同的开发人员来完成。很多时候同样的数据验证逻辑会出现在不同的层,这样就会导致代码冗余和一些管理的问题,比如说语义的一致性等。为了避免这样的情况发生,最好是将验证逻辑与相应的域模型进行绑定。
Bean Validation 为 JavaBean 验证定义了相应的元数据模型和 API。缺省的元数据是 Java Annotations,通过使用 XML 可以对原有的元数据信息进行覆盖和扩展。在应用程序中,通过使用 Bean Validation 或是你自己定义的 constraint,例如 @NotNull, @Max, @ZipCode, 就可以确保数据模型(JavaBean)的正确性。constraint 可以附加到字段,getter 方法,类或者接口上面。对于一些特定的需求,用户可以很容易的开发定制化的 constraint。Bean Validation 是一个运行时的数据验证框架,在验证之后验证的错误信息会被马上返回。
2、基础使用
1、引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
2、在实体类中加上对应的校验注解
3、使用@Valid开启数据校验
4、创建统一的异常处理器
package com.itcxc.gulimall.product.exception;
import com.itcxc.common.exception.BizCodeEnume;
import com.itcxc.common.utils.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.HashMap;
import java.util.Map;
/**
* 全局异常处理器
* @author chenxc
* @date 2021/4/13 10:39
*/
@RestControllerAdvice
@Slf4j
public class ProductExceptionControllerAdvice {
@ExceptionHandler(MethodArgumentNotValidException.class)
public R bindException(MethodArgumentNotValidException e) {
BindingResult bindingR = e.getBindingResult();
Map<String,String> map = new HashMap<>();
for (FieldError fieldError : bindingR.getFieldErrors()) {
map.put(fieldError.getField(),fieldError.getDefaultMessage());
}
return R.error(BizCodeEnume.VAILD_EXCEPTION.getCode(),BizCodeEnume.VAILD_EXCEPTION.getMsg()).put("data",map);
}
@ExceptionHandler(Exception.class)
public R handleException(Exception e){
log.error("异常信息:{}",e.getMessage());
log.error("异常类型:{}",e.getClass());
return R.error(BizCodeEnume.UNKNOW_EXCEPTION.getCode(),BizCodeEnume.UNKNOW_EXCEPTION.getMsg());
}
}
5、使用Postman请求
注:对象里面所有对象时要在里面也加注解
3、分组校验
1、创建分组的接口,不需要实现
/**
* @author chenxc
* @date 2021/4/13 11:41
*/
public interface AddGroup {
}
/**
* @author chenxc
* @date 2021/4/13 11:41
*/
public interface UpdateGroup {
}
2、将实体类的注解加上分组
3、将@Valid换为@Validated开启数据校验
注:@Valid 因为这个注解没有办法分组,所以使用Spring帮我们封装的@Validated选择分组
4、按顺序校验
1、声明一个验证序列,指定这个序列需要验证哪些组和验证的顺序
import javax.validation.GroupSequence;
@GroupSequence(value={
AddGroup.class,
UpdateGroup.class,
})
public interface ApplySequence {
}
2、使用@Validated开启数据校验
5、自定义校验
1、编写一个自定义的校验注解
注:该注解上的注解可以参考@Null这些已经实现的接口
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
/**
* @author chenxc
* @date 2021/4/13 16:11
*/
@Documented
@Constraint(
validatedBy = {ListValueConstraintValidator.class}
)
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(ListValue.List.class)
public @interface ListValue {
String message() default "{com.itcxc.common.valid.ListValue.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
int[] vals() default {};
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface List {
ListValue[] value();
}
}
2、编写一个自定义的校验器
校验器要实现接口ConstraintValidator <对应的的自定义注解,要标注的数据类型>
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.HashSet;
import java.util.Set;
/**
* @author chenxc
* @date 2021/4/13 16:29
*/
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();
if (vals.length>0){
for (int val : vals) {
set.add(val);
}
}
}
/**
* 判断校验是否成功
* @param value 需要校验的值
* @param constraintValidatorContext
* @return
*/
@Override
public boolean isValid(Integer value, ConstraintValidatorContext constraintValidatorContext) {
return set.contains(value);
}
}
3、关联自定义的校验器和自定义的校验注解
注:这个是在自定义注解上标注,指定要自定义校验器,并且校验器可以是多个
@Constraint(
validatedBy = {ListValueConstraintValidator.class}
)
然后就像使用其他注解一样使用就好了