hibernate validation
1. pom.xml配置
<properties>
<java.version>1.8</java.version>
<javax-validation.version>2.0.1.Final</javax-validation.version>
<hibernate-validator.version>6.1.5.Final</hibernate-validator.version>
<javax.el.version>3.0.0</javax.el.version>
</properties>
<dependencies>
<!-- validation -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>${javax-validation.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>${hibernate-validator.version}</version>
</dependency>
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>${javax.el.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.el</artifactId>
<version>${javax.el.version}</version>
</dependency>
</dependencies>
2. validation加载原理
这里使用的技术是 SPI全称Service Provider Interface,是Java提供的一套用来被第三方实现或者扩展的接口,它可以用来启用框架扩展和替换组件。 SPI的作用就是为这些被扩展的API寻找服务的实现。
在这里服务会寻找实现上图最后一个红框内javax.validation.spi.ValidationProvider这个接口的类。在存在一个hibernate-validator-6.1.5.Final.jar!/META-INF/services/javax.validation.spi.ValidationProvider文件内容是org.hibernate.validator.HibernateValidator
所以红框里的ServiceLoader<ValidationProvider> loader得到了一个ValidationProvider的实现(org.hibernate.validator.HibernateValidator)
再回去看javax.validation.Validation
3. 默认消息文件的位置
hibernate-validator-6.1.5.Final.jar!/org/hibernate/validator下面的ValidationMessages_xx.properties
4. annotation约束的实现类的位置
hibernate-validator-6.1.5.Final.jar!/org/hibernate/validator/internal/constraintvalidators/的bv和mv下面
5. annotation约束的实现类的绑定关系
绑定关系类是org.hibernate.validator.internal.metadata.core.ConstraintHelper
ConstraintHelper是在上面最后一个红框里ValidatorFactoryImpl的构造函数里被实例化的(ConstraintHelper constraintHelper = ConstraintHelper.forAllBuiltinConstraints();)
如何绑定关系的呢?在ConstraintHelper构造函数里调用了如下代码
putBuiltinConstraint( tmpConstraints, NotNull.class, NotNullValidator.class );
说明NotNull这个约束的验证器实现类是 NotNullValidator,在构造函数的最后把这些绑定函数都给了enabledBuiltinConstraints对象
当调用验证的时候 会通过会调用isBuiltinConstraint函数来判断是否该annotation约束是否是包里的还是自定义的。
并通过依次调用getAllValidatorDescriptors=>getDefaultValidatorDescriptors这2个函数从enabledBuiltinConstraints获取验证的实现类
6. 验证的实现类的获取
实现类是org.hibernate.validator.internal.engine.ValidatorImpl
通过调用hibernate-validator-6.1.5.Final.jar的ValidatorFactoryImpl的getValidator()获取的
7.控制校验顺序
通过@GroupSequence指定分组顺序
public class UserVo {
/**
* 单体查询校验分组
*/
@GroupSequence({Default.class, Extended.class})
public interface QueryValidatorGroup {
public interface Default {
}
public interface Extended {
}
}
/**
* 列表查询校验分组
*/
@GroupSequence({Default1.class, Extended1.class})
public interface ListValidatorGroup {
public interface Default1 {
}
public interface Extended1 {
}
}
@NotEmpty(groups={Default1.class})
private String userId;
@NotEmpty(groups={Extended.class, Extended1.class})
private String userNo;
@NotEmpty(groups={Default.class})
private String userName;
@NotEmpty(groups={Extended.class})
private String userNickName;
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserNickName() {
return userNickName;
}
public void setUserNickName(String userNickName) {
this.userNickName = userNickName;
}
public String getUserNo() {
return userNo;
}
public void setUserNo(String userNo) {
this.userNo = userNo;
}
}
这里有2个分组,分别是QueryValidatorGroup和ListValidatorGroup,每组都要先验证带有Default 的字段,如果Default字段只要有一个验证失败的时候,就不去做带有Extended字段的验证了。
验证代码:
package com.example.demo;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import org.hibernate.validator.HibernateValidator;
import org.hibernate.validator.HibernateValidatorConfiguration;
import com.example.demo.UserVo.ListValidatorGroup;
import com.example.demo.UserVo.QueryValidatorGroup;
public class ValidatorTest {
public static void main(String[] args) {
// 验证单体查询校验分组 这里Default字段是userName,Extended字段是(userNo,userNickName)
// 其中 userNo既是单体查询校验分组的Extended也是列表查询校验分组的Extended
// 测试1
UserVo userVo = new UserVo();
HibernateValidatorConfiguration configure = Validation.byProvider(HibernateValidator.class).configure();
ValidatorFactory validatorFactory = configure.failFast(false).buildValidatorFactory();
// 根据validatorFactory拿到一个Validator
Validator validator = validatorFactory.getValidator();
// 使用validator对结果进行校验
Set<ConstraintViolation<UserVo>> result = validator.validate(userVo, QueryValidatorGroup.class);
System.out.println("测试1");
// 对结果进行遍历输出
result.stream().map(v -> v.getPropertyPath() + " " + v.getMessage() + ": " + v.getInvalidValue())
.forEach(System.out::println);
// 测试2
System.out.println("测试2");
UserVo userVo2 = new UserVo();
userVo2.setUserName("a");
configure = Validation.byProvider(HibernateValidator.class).configure();
validatorFactory = configure.failFast(false).buildValidatorFactory();
Set<ConstraintViolation<UserVo>> result2 = validator.validate(userVo2, QueryValidatorGroup.class);
// 对结果进行遍历输出
result2.stream().map(v -> v.getPropertyPath() + " " + v.getMessage() + ": " + v.getInvalidValue())
.forEach(System.out::println);
// 验证列表查询校验分组
// 测试3
System.out.println("测试3");
UserVo userVo3 = new UserVo();
configure = Validation.byProvider(HibernateValidator.class).configure();
validatorFactory = configure.failFast(false).buildValidatorFactory();
Set<ConstraintViolation<UserVo>> result3 = validator.validate(userVo3, ListValidatorGroup.class);
// 对结果进行遍历输出
result3.stream().map(v -> v.getPropertyPath() + " " + v.getMessage() + ": " + v.getInvalidValue())
.forEach(System.out::println);
// 测试4
System.out.println("测试4");
UserVo userVo4 = new UserVo();
userVo4.setUserId("a");
configure = Validation.byProvider(HibernateValidator.class).configure();
validatorFactory = configure.failFast(false).buildValidatorFactory();
Set<ConstraintViolation<UserVo>> result4 = validator.validate(userVo4, ListValidatorGroup.class);
// 对结果进行遍历输出
result4.stream().map(v -> v.getPropertyPath() + " " + v.getMessage() + ": " + v.getInvalidValue())
.forEach(System.out::println);
}
}
运行结果
测试1
userName 不能为空: null
测试2
userNickName 不能为空: null
userNo 不能为空: null
测试3
userId 不能为空: null
测试4
userNo 不能为空: null
l
UserVo也可以换个写法
package com.example.demo;
import javax.validation.GroupSequence;
import javax.validation.constraints.NotEmpty;
public class UserVo {
/**
* 单体查询校验分组
*/
@GroupSequence({QueryValidatorGroup.Default.class, QueryValidatorGroup.Extended.class})
public interface QueryValidatorGroup {
public interface Default {
}
public interface Extended {
}
}
/**
* 列表查询校验分组
*/
@GroupSequence({ListValidatorGroup.Default.class, ListValidatorGroup.Extended.class})
public interface ListValidatorGroup{
public interface Default {
}
public interface Extended {
}
}
@NotEmpty(groups={ListValidatorGroup.Default.class})
private String userId;
@NotEmpty(groups={QueryValidatorGroup.Extended.class, ListValidatorGroup.Extended.class})
private String userNo;
@NotEmpty(groups={QueryValidatorGroup.Default.class})
private String userName;
@NotEmpty(groups={QueryValidatorGroup.Extended.class})
private String userNickName;
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserNickName() {
return userNickName;
}
public void setUserNickName(String userNickName) {
this.userNickName = userNickName;
}
public String getUserNo() {
return userNo;
}
public void setUserNo(String userNo) {
this.userNo = userNo;
}
}