Bean Validator简介
JavaBean 验证主要是 POJO、Dao、Service、Controller
JCP和JSR:JCP(Java Community Process)使各方参与定义 Java 特征和未来版本的正式过程,JCP 使用 JSR(Java Spectification Request,Java规范请求)作为正式规范文档,描述被提议加入到 Java 体系中的规范和技术
- JSR303 — Bean Validator 1.0 — Hibernate Validator 4.3.1.Final
- JSR349 — Bean Validator 1.1 — Hibernate Validator 5.1.1.Final
- JSR380 — Bean Validator 2.0 — Hibernate Validator 6.0.1.Final
Spring Validator 在 Hibernate Validator 基础上进行二次封装,以满足 Spring 环境中更简单、高效地对数据进行验证
Spring Validator使用
基础校验
基础校验流程
-
pom.xml 文件引入 hibernate-validator 依赖
<dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> </dependency>
-
进行校验的 Controller 标记 @Validated 注解
-
URL以“?”拼接参数:/check-param/url-params-01?name=hefery&age=18&gender=man
@RequestMapping(value = "/url-params-01", method = RequestMethod.GET) public String testUrlParams01( @RequestParam("name") String name, @RequestParam("age") @Range(min = 1, max = 18, message = "年龄在1~18之间") Integer age, @RequestParam(value = "gender", required = false) String gender ) { UserDTO user = new UserDTO(); user.setName(name); user.setAge(age); user.setGender(gender); return JSON.toJSONString(user); }
-
URL以“/”拼接参数:/check-param/url-params-03/hefery/18/man
@RequestMapping(value = {"/url-params-03/{name}/{age}/{gender}","/url-params-03/{name}/{gender}"}, method = RequestMethod.POST) public String testUrlParams03( @PathVariable("name") String name, @PathVariable(value = "age", required = false) @Range(min = 1, max = 18, message = "年龄在1~18之间") Integer age, @PathVariable("gender") String gender ) { UserDTO user = new UserDTO(); user.setName(name); user.setGender(gender); user.setAge(age); return JSON.toJSONString(user); }
-
参数写在请求体:/check-param/json-params-01
在需要校验的 UserDTO 前添加 @Valid 或 @Validated 注解@RequestMapping(value = "/json-params-01", method = RequestMethod.GET) public String testJsonParams01(@RequestBody @Valid UserDTO user) { return JSON.toJSONString(user); } public class UserDTO { private String name; @Range(min = 1, max = 18, message = "年龄在1~18之间") private Integer age; private String gender; }
-
基础校验注解
-
空值类
- @Null
- @NotNull:
@NotNull(message = "用户ID不能为空")
- @NotEmpty:
@NotEmpty(message = "用户名不能为空")
- @NotBlank:
@NotBlank(message = "密码不能为空")
-
范围类:
-
@Length:
- min:最小长度
- max:最大长度
-
@Min:
@Min(value = 18, message = "年龄不能小于18岁")
- value:最小值
-
@Max:
@Max(value = 30, message = "年龄不能超过30岁")
- value:最大值
-
@Size:
@Size(min = 1, max = 30, message = "不能少于1位好友")
- min:最小长度
- max:最大长度
-
@Range:
@Range(min = 1, max = 30, message = "必须在 1~30 之间")
-
-
值判断:
- @Digits:数字
- @AssertTrue:必须是 true
- @AssertFalse:必须是 false
-
日期类:
- @Future:将来的日期
- @Past:
@Past(message = "日期不能为当前日期和未来日期")
-
其他类:
- @Email:
@Email(message = "邮箱必须有效")
- @Pattern:符合正则
- @Valid
- @Email:
-
高级:
- 分组:适用于不同场景的使用
- 在实体类中定义场景接口
// 登录场景 public interface LoginGroup {} // 注册场景 public interface RegisterGroup {}
- 在场景所需字段的注解中添加分组信息
/** 用户名 */ @NotEmpty(message = "用户名不能为空", groups = UserInfo.LoginGroup.class) private String username; /** 密码 */ @NotBlank(message = "密码不能为空", groups = UserInfo.LoginGroup.class) @Length(min = 6, max = 20, message = "密码成都不能少于6位,以及不能多于20位") private String password;
- 验证时,加入场景接口
// 验证器验证 userInfo 对象 resultSet = validator.validate( userInfo, UserInfo.RegisterGroup.class, UserInfo.LoginGroup.class );
- 在实体类中定义场景接口
- 分组排序:决定先进行 分组1 校验,再进行 分组2 校验
- 在实体类中定义分组排序的接口
@GroupSequence({ LoginGroup.class, RegisterGroup.class, Default.class }) public interface Group {}
- 在场景所需字段的注解中添加分组信息
// 验证器验证 userInfo 对象 resultSet = validator.validate( userInfo, UserInfo.Group.class );
- 在实体类中定义分组排序的接口
- 分组:适用于不同场景的使用
@NotEmpty、@NotBlank区别:
- @NotEmpty(message = "用户名不能为空") // 指的是 userInfo.setUsername("") 不会报错
空的:没有内容- @NotBlank(message = "密码不能为空") // 指的是 userInfo.setPassword("") 会报错
空白:自动去掉字符串前后空格后验证是否为空
级联校验
校验 Bean 中嵌套的 Bean,如 UserDTO 中的 SchoolDTO 中的 schoolName 属性(外层加@Valid,里层加基础校验)
- 在 SchoolDTO 中的 schoolName 属性添加基础校验注解
- 在 UserDTO 中的 schoolDTO 属性标注 @Valid 注解
@RequestMapping(value = "/json-params-02", method = RequestMethod.GET)
public String testJsonParams02(@RequestBody @Valid UserDTO user) {
return JSON.toJSONString(user);
}
public class UserDTO {
private String name;
@Range(min = 1, max = 18, message = "年龄在1~18之间")
private Integer age;
private String gender;
@Valid
private SchoolDTO schoolDTO;
}
public class SchoolDTO {
@Length(min = 2, max = 3, message = "schoolName长度在2~3之间")
private String schoolName;
private String schoolAddress;
}
分组校验
自定义校验
// 校验执行器
ExecutableValidator executableValidator = validator.forExecutables();
-
校验参数
public class UserInfoService { /** * UserInfo 作为输入参数 * @param userInfo */ public void setUserInfo(@Valid UserInfo userInfo) {} } @Test public void test() throws NoSuchMethodException { // 初始化验证器 Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); // 校验执行器 ExecutableValidator executableValidator = validator.forExecutables(); // 待验证对象 UserInfoService userInfoService = new UserInfoService(); // 待验证方法 Method setUserInfoMethod = userInfoService.getClass().getMethod("setUserInfo", UserInfo.class); // 方法输入参数 Object[] paramObjects = new Object[]{new UserInfo()}; // 对方法的输入参数进行校验 Set<ConstraintViolation<UserInfoService>> otherSet; otherSet = executableValidator.validateParameters( userInfoService, setUserInfoMethod, paramObjects ); }
-
校验返回值
public class UserInfoService { /** * UserInfo 作为返回值 * @return */ public UserInfo getUserInfo() { return new UserInfo(); } } @Test public void test() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { // 初始化验证器 Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); // 校验执行器 ExecutableValidator executableValidator = validator.forExecutables(); // 待验证对象 UserInfoService userInfoService = new UserInfoService(); // 待验证方法 Method getUserInfoMethod = userInfoService.getClass().getMethod("getUserInfo"); // 待验证返回值 Object returnValue = getUserInfoMethod.invoke(userInfoService); // 对方法的输入参数进行校验 Set<ConstraintViolation<UserInfoService>> otherSet; otherSet = executableValidator.validateReturnValue( userInfoService, getUserInfoMethod, returnValue ); }
-
校验构造方法
public class UserInfoService { /** * 空参构造 */ public UserInfoService() {} /** * 有参构造 * @param userInfo */ public UserInfoService(UserInfo userInfo) { } } @Test public void test() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { // 初始化验证器 Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); // 校验执行器 ExecutableValidator executableValidator = validator.forExecutables(); // 待验证对象 UserInfoService userInfoService = new UserInfoService(); // 待验证构造方法 Constructor constructor = UserInfoService.class.getConstructor(UserInfo.class); // 待验证构造函数 Object[] paramObjects = new Object[]{new UserInfo()}; // 对方法的输入参数进行校验 Set<ConstraintViolation<UserInfoService>> otherSet; otherSet = executableValidator.validateConstructorParameters( constructor, paramObjects ); }
使用 Bean Validation 校验机制,对基本数据类型进行校验,方法是在实体类属性上使用注解标识校验方式,最后在 Controller 中具体方法的形参里添加@Vlidated注解。Bean Validation 校 验有一个缺点是,我们的数据校验是在 Java 实体类里进行约束的,如果我们有多个处理器方法需要用到同一个实体类,那么定义在实体类属性上的校验规则就不好划分了,有的处理器只需要校验一个属性,而有的处理器需要校验多个属性,我们不可能为每一个处理器都创建一个实体类。解决的方法在上一篇日志里也说到,使用分组校验方式,除此之外,还可以使用 Spring 的 Validator 接口校验,它允许我们在外部指定某一对象的校验规则