学习Validator验证框架总结
在项目开发中许多地方需要加以验证,对于使用if-else简单粗暴一个一个验证,spring的validation封装了Javax ValidationI校验参数,大大缩减了代码量。
以前的分层验证,从controller到落入数据库,一层一层验证,代码重复、冗余。
Javax ValidationI使用Java Bean验证通过注解将约束添加到域模型中,将验证逻辑从代码中分离出来。
Javax ValidationI的依赖:
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.16.Final</version>
</dependency>
springboot对Javax ValidationI封装,依赖变成
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
一、初级注解
JSR提供的验证注解:
@Null 被注释的元素必须为 null
@NotNull 被注释的元素必须不为 null
@AssertTrue 被注释的元素必须为 true
@AssertFalse 被注释的元素必须为 false
@Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值  
@Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max=, min=) 被注释的元素的大小必须在指定的范围内
@Digits (integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
@Past 被注释的元素必须是一个过去的日期
@Future 被注释的元素必须是一个将来的日期
@Pattern(regex=,flag=) 被注释的元素必须符合指定的正则表达式
Validator提供的验证注解:
@NotBlank(message =) 验证字符串非null,且长度必须大于0
@Email 被注释的元素必须是电子邮箱地址
@Length(min=,max=) 被注释的字符串的大小必须在指定的范围内
@NotEmpty 被注释的字符串的必须非空
@Range(min=,max=,message=) 被注释的元素必须在合适的范围内
二、创建验证器
/**
* 验证测试类
*/
public class ValidationTest {
// 验证器对象
private Validator validator;
// 待验证对象
private UserInfo userInfo;
// 验证结果集合
private Set<ConstraintViolation<UserInfo>> set;
/**
* 初始化操作
*/
@Before
public void init() {
// 初始化验证器
validator = Validation.buildDefaultValidatorFactory()
.getValidator();
// 初始化待验证对象 - 用户信息
userInfo = new UserInfo();
}
/**
* 结果打印
*/
@After
public void print() {
set.forEach(item -> {
// 输出验证错误信息
System.out.println(item.getMessage());
});
}
@Test
public void nullValidation() {
// 使用验证器对对象进行验证
set = validator.validate(userInfo);
}
}
三、中级约束
中级约束中分为分组约束、组序列
public class UserInfo {
// 登录场景
public interface LoginGroup {}
// 注册场景
public interface RegisterGroup {}
/**
* 用户ID
*/
@NotNull(message = "用户ID不能为空",
groups = LoginGroup.class)
private String userId;
/**
* 用户名
* NotEmpty 不会自动去掉前后空格
*/
@NotEmpty(message = "用户名称不能为空",groups = RegisterGroup.class)
private String userName;
/**
* 用户密码
* NotBlank 自动去掉字符串前后空格后验证是否为空
*/
@NotBlank(message = "用户密码不能为空")
@Length(min = 6, max = 20,
message = "密码长度不能少于6位,多于20位")
private String passWord;
}
/**
* 分组验证测试方法
*/
@Test
public void groupValidation() {
set = validator.validate(userInfo,
UserInfo.RegisterGroup.class,
UserInfo.LoginGroup.class);
}
组排序:
public class UserInfo {
// 登录场景
public interface LoginGroup {}
// 注册场景
public interface RegisterGroup {}
// 组排序场景
@GroupSequence({
LoginGroup.class,
RegisterGroup.class,
Default.class
})
public interface Group {}
/**
* 用户ID
*/
@NotNull(message = "用户ID不能为空",
groups = LoginGroup.class)
private String userId;
/**
* 用户名
* NotEmpty 不会自动去掉前后空格
*/
@NotEmpty(message = "用户名称不能为空",
groups = RegisterGroup .class)
private String userName;
/**
* 用户密码
* NotBlank 自动去掉字符串前后空格后验证是否为空
*/
@NotBlank(message = "用户密码不能为空")
@Length(min = 6, max = 20,
message = "密码长度不能少于6位,多于20位")
private String passWord;
}
/**
* 组序列测试
*/
@Test
public void groupSequenceValidation() {
set = validator.validate(userInfo,
UserInfo.Group.class);
}
三、高级约束
高级约束是对参数、返回值的约束。
使用注解:
javax:@Valid
spring:@Validated
在检验参数、返回值是否符合规范时,使用@Validated或者@Valid在基本验证功能上没有太多区别。但是在分组、注解地方、嵌套验证等功能上两个有所不同。
@Validated:提供了一个分组功能,可以在入参、返回值验证时,根据不同的分组采用不同的验证机制。
@Valid:不支持分组功能。
注解地方:
@Validated:可以用在类型、方法和方法参数上。但是不能用在成员属性(字段)上。
@Valid:可以用在方法、构造函数、方法参数和成员属性(字段)上。
/**
* 用户信息
*/
public class UserInfoService {
/**
* UserInfo 作为输入参数
* @param userInfo
*/
public void setUserInfo(@Valid UserInfo userInfo) {}
/**
* UserInfo 作为输出参数
* @return
*/
public @Valid UserInfo getUserInfo() {
return new UserInfo();
}
}
/**
* 验证测试类
*/
public class ValidationTest {
// 验证器对象
private Validator validator;
// 待验证对象
private UserInfo userInfo;
// 验证结果集合
private Set<ConstraintViolation<UserInfoService>> otherSet;
/**
* 初始化操作
*/
@Before
public void init() {
// 初始化验证器
validator = Validation.buildDefaultValidatorFactory()
.getValidator();
// 初始化待验证对象 - 用户信息
userInfo = new UserInfo();
}
/**
* 结果打印
*/
@After
public void print() {
set.forEach(item -> {
// 输出验证错误信息
System.out.println(item.getMessage());
});
}
/**
* 对方法输入参数进行约束注解校验
*/
@Test
public void paramValidation() throws NoSuchMethodException {
// 获取校验执行器
ExecutableValidator executableValidator =
validator.forExecutables();
// 待验证对象
UserInfoService service = new UserInfoService();
// 待验证方法
Method method = service.getClass()
.getMethod("setUserInfo", UserInfo.class);
// 方法输入参数
Object[] paramObjects = new Object[]{new UserInfo()};
// 对方法的输入参数进行校验
otherSet = executableValidator.validateParameters(
service,
method,
paramObjects);
}
/**
* 对方法返回值进行约束校验
*/
@Test
public void returnValueValidation()
throws NoSuchMethodException,
InvocationTargetException, IllegalAccessException {
// 获取校验执行器
ExecutableValidator executableValidator =
validator.forExecutables();
// 构造要验证的方法对象
UserInfoService service = new UserInfoService();
Method method = service.getClass()
.getMethod("getUserInfo");
// 调用方法得到返回值
Object returnValue = method.invoke(service);
// 校验方法返回值是否符合约束
otherSet = executableValidator.validateReturnValue(
service,
method,
returnValue);
}
}
在controller中验证入参一般使用@Validated
@RequestMapping(method = RequestMethod.POST)
public UserInfo create(@RequestBody @Validated( { RegisterGroup.class }) UserInfo userInfo) {
return userService.create(userInfo);
}
@RequestMapping(method = RequestMethod.GET)
public UserInfo getUserById(@NotNull(message = "id不能为空") int userId) {
return userService.getUserById(userId);
}
四、自定义约束注解
/**
* 自定义手机号约束注解
*/
@Documented
// 注解的作用目标
@Target({ElementType.FIELD})
// 注解的保留策略
@Retention(RetentionPolicy.RUNTIME)
// 不同之处:于约束注解关联的验证器
@Constraint(validatedBy = PhoneValidator.class)
public @interface Phone {
// 约束注解验证时的输出信息
String message() default "手机号校验错误";
// 约束注解在验证时所属的组别
Class<?>[] groups() default {};
// 约束注解的有效负载(严重程度)
Class<? extends Payload>[] payload() default {};
}
/**
* 自定义手机号约束注解关联验证器
*/
public class PhoneValidator
implements ConstraintValidator<Phone, String> {
/**
* 自定义校验逻辑方法
* @param value
* @param context
* @return
*/
@Override
public boolean isValid(String value,
ConstraintValidatorContext context) {
// 手机号验证规则:158后头随便
String check = "158\\d{8}";
Pattern regex = Pattern.compile(check);
// 空值处理
String phone = Optional.ofNullable(value).orElse("");
Matcher matcher = regex.matcher(phone);
return matcher.matches();
}
}
自定义注解使用:
public class UserInfo{
/**
* 手机号
*/
@Phone(message = "手机号不是158后头随便")
private String phone;
}

浙公网安备 33010602011771号