本文参考自:http://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/
声明方式
1. Field-Level
基于属性的校验
public class User{
[@NotNull](https://my.oschina.net/notnull)
private String name;
}
2. Property-Level
基于getter的校验
public class User{
private String name;
[@NotNull](https://my.oschina.net/notnull)
public String getName(){
return name;
}
}
3. Container Element
容器校验
public class User{
// 集合中元素须长度 > 2
private List<@Length(min = 3) String> roles;
}
4. Class-Level
类校验
// 声明注解
@Target({TYPE, ANNOTATION_TYPE})
@Retention(RUNTIME)
@Constraint(validatedBy = {UserNameNotMatchValidator.class})
@Documented
public @interface UserNameNotMatch{
String message() default "用户不匹配";
Class<?>[] groups() default {};
String allowName() default "admin";
Class<? extends Payload>[] payload() default {};
}
// 编写具体的校验类
public class UserNotMatchValidator implements ConstraintValidator<UserNotMatch, User> {
private String allowName;
@Override
public void initialize(UserNotMatch constraintAnnotation) {
allowName = constraintAnnotation.allowName();
}
@Override
public boolean isValid(User user, ConstraintValidatorContext constraintValidatorContext) {
if (allowName.equals(user.getName())) {
return true;
} else {
// 屏蔽默认错误信息
constraintValidatorContext.disableDefaultConstraintViolation();
constraintValidatorContext.buildConstraintViolationWithTemplate("普通用户不允许操作").addConstraintViolation();
return false;
}
}
}
@UserNameNotMatch
public class User{
private String name;
}
5. Constraint inheritance
约束继承:当一个类继承另一个类或者实现一个接口,超类中声明的约束与该类本身声明的约束同样有效
public class User{
@NotNull
private String name;
}
public class Person extends User{
@NotNull
private String sex;
}
6. Object graphs
public class Role{
@NotNull
private String auth;
}
public class User{
@Valid // 标记该注解才会进行校验Role对象,否则只校验User.role
@NotNull
private Role role;
}
public class Role{
private String auth;
}
public class User{
private Set<@NotNull @Valid Role> roleSet;
}
开始验证
手动创建Validator
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
validator = factory.getValidator();
Spring Boot
引入spring-boot-starter-web,该依赖中包含 hibernate-validator,自动装配Validator Bean
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
注入Validator (javax.validation)
@Autowired
private Validator validator
验证
Validator#validate(T clazz): 校验类(@Valid 仅在使用该方法时有效)
Validator#validateProperty(T clazz,String propertyName): 校验属性
Validator#validateValue(T clazz,String propertyName, String value): 校验属性值
示例
Set<ConstraintViolation<T>> violationSet = validator.validate(tClass);
violationSet.forEach(userConstraintViolation -> log.warn("Validation Err Bean={}, Property = {},Msg = {}", userConstraintViolation.getRootBeanClass(), userConstraintViolation.getPropertyPath(), userConstraintViolation.getMessage()));
ConstraintViolation
ConstraintViolation#getPropertyPath(): 属性名称
ConstraintViolation#getMessage(): 错误信息
Validation API 提供的约束
- @AssertFalse 检查被标记元素是否为 false
- @AssertFalse 检查被标记元素是否为 true
- @DecimalMax(value=, inclusive=) 检查被标记的元素是否小于(或者等于)指定的最大值。inclusive=false 检查被标记的元素是否小于指定的最大值;如果inclusive=true 检查被标记的值是否小于或者等于指定的最大值(支持的数据类型:
BigDecimal
,BigInteger
,CharSequence
,byte
,short
,int
,long
) - @Digits(integer=, fraction=) 检查被标记的元素,整数位数在integer范围内,小数位数在fraction范围内(支持的数据类型:
BigDecimal
,BigInteger
,CharSequence
,byte
,short
,int
,long
) - @Email(regexp=,flags=) 检查被标记的元素是否是一个有效的电子邮件地址。regexp为正则表达式;flags为正则表达式标志(参考:https://blog.csdn.net/hsttmht/article/details/6964018)
- @Future 检查被标记日期是否在将来
- @FutureOrPresent 检查被标记日期是否在将来或者当前
- @Max(value=) 检查被标记的元素是否小于或者等于指定的最大值(支持的数据类型:
BigDecimal
,BigInteger
,byte
,short
,int
,long
) - @Min(value=) 检查被标记的元素是否大于或者等于指定的最小值(支持的数据类型:
BigDecimal
,BigInteger
,byte
,short
,int
,long
) - @NotBlank 检查被标记的字符串是否不为空且trim()后的字符串长度大于0
- @NotEmpty 检查被标记的值是否不为Null且长度/大小大于0(支持的数据类型:
CharSequence
,Collection
,Map and arrays
) - @NotNull 检查被标记的元素是否不为Null
- @Negative 检查被标记的元素是否为负数(支持的数据类型:
BigDecimal
,BigInteger
,byte
,short
,int
,long
) - @NegativeOrZero 检查被标记的元素是否为负数或者0(支持的数据类型:
BigDecimal
,BigInteger
,byte
,short
,int
,long
) - @Null 检查被标记的值是否为Null
- @Past 检查被标记日期是否在过去
- @PastOrPresent 检查被标记日期是否在过去或者当前
- @Pattern(regex=, flags=) 检查被标记的字符串是否与指定的正则表达式匹配;flags为正则表达式标志(参考:https://blog.csdn.net/hsttmht/article/details/6964018)
- @Positive 检查被标记的元素是否为正数(支持的数据类型:
BigDecimal
,BigInteger
,byte
,short
,int
,long
) - @PositiveOrZero 检查被标记的元素是否为正数或者0(支持的数据类型:
BigDecimal
,BigInteger
,byte
,short
,int
,long
) - @Size(min=, max=) 检查被标记的元素的大小是否在min和max之间,包括min、max(支持的数据类型:
CharSequence
,Collection
,Map and arrays
)
错误消息
默认错误消息
ValidationMessages.properties
默认情况下使用JVM默认的语言环境,也可将本地化的ValidationMessages.properties添加到classpath下,覆盖默认语言环境下的配置
使用消息表达式
@Size(
min = 2,
max = 14,
message = "The license plate '${validatedValue}' must be between {min} and {max} characters long"
)
private String licensePlate;
Car car = new Car();
car.setLicensePlate("A");
message = validator.validateProperty( car, "licensePlate" )
.iterator()
.next()
.getMessage();
assertEquals(
"The license plate 'A' must be between 2 and 14 characters long",
message
);
分组约束
Requesting groups 组约束
public class Car {
@NotNull
private String manufacturer;
@NotNull
@Size(min = 2, max = 14)
private String licensePlate;
@Min(2)
private int seatCount;
@AssertTrue(
message = "The car has to pass the vehicle inspection first",
groups = CarChecks.class
)
private boolean passedVehicleInspection;
}
//manufacturer、licensePlate、seatCount则不会被校验
constraintViolations = validator.validate( car, CarChecks.class );
assertEquals( 1, constraintViolations.size() );
assertEquals(
"The car has to pass the vehicle inspection first",
constraintViolations.iterator().next().getMessage()
);
Group inheritance 组继承
定义Group
public interface Group {
}
定义父类
public class GroupB {
@NotNull(groups = Group.class)
private String title;
}
定义子类
public class GroupA extends GroupB{
@NotEmpty
private String name;
@NotNull(groups = Group.class)
private String desc;
}
验证
constraintViolations = validator.validate( group,Group.class );
assertThat( constraintViolations ).extracting( "message" ).containsOnly(
"title must not be null",
"desc must not be null"
);
Defining group sequences 定义组序列
按照组序列的定义顺序,其中一个约束验证失败,其后的约束都不会得到验证
public interface Age {
}
public interface Sing {
}
@GroupSequence({Age.class, Sing.class})
public interface OrderChecks {
}
定义校验类
public class Person {
@Min(value = 18, groups = Age.class)
private Long age;
@AssertTrue(groups = Sing.class)
private boolean sing;
}
验证
Person person = new Person();
person.setAge(17L);
person.setSing(false);
validate(person, OrderChecks.class);
Redefining the default group sequence 重新定义组序列(@GroupSequence)
重新定义校验类
被@GroupSequence
标记的类必须声明在@GroupSequence
中,如示例中的Person.class
@GroupSequence({Sing.class, Age.class, Person.class})
public class Person {
@Min(value = 18, groups = Age.class)
private Long age;
@AssertTrue(groups = Sing.class)
private boolean sing;
}
验证
Person person = new Person();
person.setAge(17L);
person.setSing(false);
validate(person);
Redefining the default group sequence 重新定义组序列(@GroupSequenceProvider)
public class PersonGroupSequenceProvider implements DefaultGroupSequenceProvider<Person> {
@Override
public List<Class<?>> getValidationGroups(Person person) {
List<Class<?>> defaultGroupSequence = new ArrayList<>();
// 必须包含类本身
defaultGroupSequence.add(Person.class);
if (null != person) {
defaultGroupSequence.add(Sing.class);
defaultGroupSequence.add(Age.class);
}
return defaultGroupSequence;
}
}
@GroupSequenceProvider(PersonGroupSequenceProvider.class)
public class Person {
@Min(value = 18, groups = Age.class)
private Long age;
@AssertTrue(groups = Sing.class)
private boolean sing;
}
Group conversion 组转换
public class Person {
@Min(value = 18, groups = Age.class)
private Long age;
@AssertTrue(groups = Sing.class)
private boolean sing;
@NotEmpty(groups = Sing.class)
private List<String> songs;
}
public class Company {
@Valid
@ConvertGroup(from = Sing.class, to = Age.class)
private Person person;
}
验证
Company company = new Company();
Person person = new Person();
person.setAge(17L);
person.setSing(false);
person.addSong("Trouble is a friend");
company.setPerson(person);
// 验证时传递的组是Sign.class,由于Company#person 定义了@ConvertGroup(from = Sing.class, to = Age.class),最终验证的组是Age.class
validate(company, Sing.class);