携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第8天,点击查看活动详情
引入
Hibernate Validator 是 Bean Validation 的实现,Hibernate Validator 内置了 JSR303/JSR380 中所有的 constraint 的实现,还额外提供了很多自定义的 constraint。
Spring-boot
spring-boot-starter-web就包含了hibernate validator。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
spring MVC
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.17.Final</version>
</dependency>
验证bean
validator的基本用法和lombok一样。添加到实体类的属性上。如果不符合属性的要求就会抛出错误。
例如:
@Data
@AllArgsConstructor
public class User {
@NotNull
private String id;
@NotBlank
@Size(max = 20)
private String name;
@NotNull
@Pattern(regexp = "[A-Z][a-z][0-9]")
private String password;
@NotNull
private Integer age;
@Max(10)
@Min(1)
private Integer level;
}
验证
如果不通过spring,validator是无法直接劫持实例化的过程。只能通过对应的validator对象的方法来验证对应的对象。
如:
User user = new User(null, "", "123", null, 0);
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
validator = factory.getValidator();
// 验证所有bean的所有约束
Set<ConstraintViolation<User>> constraintViolations = validator.validate(user);
// 验证单个属性
Set<ConstraintViolation<User>> constraintViolations2 = validator.validateProperty(user, "name");
// 检查给定类的单个属性是否可以成功验证
Set<ConstraintViolation<User>> constraintViolations3 = validator.validateValue(User.class, "password", "sa!");
constraintViolations.forEach(constraintViolation -> System.out.println(constraintViolation.getMessage()));
constraintViolations2.forEach(constraintViolation -> System.out.println(constraintViolation.getMessage()));
constraintViolations3.forEach(constraintViolation -> System.out.println(constraintViolation.getMessage()));
结果
不能为Null
不能为空
需要匹配正则表达式"[A-Z][a-z][0-9]"
最小为1
验证方法
验证方法可以通过将注解加到对应的位置来验证参数或者返回值。
验证参数
可以通过将注解加到参数前面,然后通过验证器来验证某个方法是是否合法。
例如:
package org.hibernate.validator.referenceguide.chapter03.parameter;
public class RentalStation {
public RentalStation(@NotNull String name) { //...
}
public void rentCar( @NotNull Customer customer, @NotNull @Future Date startDate, @Min(1) int durationInDays) {
//...
}
}
- 验证构造方法的参数不能为空,
- rentCar方法的第一个参数不为空,
- 第二个参数必须为当前时间的位来,
- 第三个参数最小只能为1。
验证返回值
同样的,将注解加到对应的方法上可以验证返回值是否合法。
例如:
package org.hibernate.validator.referenceguide.chapter03.returnvalue;
public class RentalStation {
@ValidRentalStation
public RentalStation() {
//...
}
@NotNull @Size(min = 1)
public List<@NotNull Customer> getCustomers() {
//...
return null;
}
}
这里确保了:
- 任何新创建的RentalStation对象都必须满足@ValidRentalStation约束
- getCustomers()返回的客户列表不能为空,并且必须至少包含1个元素
- getCustomers()返回的客户列表不能包含空对象。
验证
方法约束的验证是使用ExecutableValidator接口完成的。
示例如下:
Car object = new Car( "Morris" ); Method method = Car.class.getMethod( "drive", int.class );// 1. 获取executableValidator对象
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
ExecutableValidator executableValidator = factory.getValidator().forExecutables();
// 2. 验证方法参数
Car object = new Car( "Morris" );
Method method = Car.class.getMethod( "drive", int.class );
Object[] parameterValues = { 80 };
Set<ConstraintViolation<Car>> violations = executableValidator.validateParameters( object, method, parameterValues);
Class<? extends Annotation> constraintType = violations.iterator() .next() .getConstraintDescriptor() .getAnnotation()
.annotationType();
// 3. 验证返回值
Constructor<Car> constructor = Car.class.getConstructor( String.class, String.class );
Car createdObject = new Car( "Morris", null );
Set<ConstraintViolation<Car>> violations = executableValidator .validateConstructorReturnValue( constructor, createdObject);
Class<? extends Annotation> constraintType = violations.iterator() .next() .getConstraintDescriptor() .getAnnotation().annotationType();
注意:ExecutableValidator接口总共提供了四个方法:
validateParameters():用来验证方法参数。validateReturnValue():用来验证方法返回值。validateConstructorParameters():用来验证构造器参数。validateConstructorReturnValue():用来验证构造器返回值。
spring结合
可以看到,上面的验证非常复杂,但是有了spring的aop帮助,我们只需要配置以下,就可以让spring自动为我们进行验证。我们只需要在方法或者参数上添加约束注解即可。
class方式
基本配置
如果只需要验证实体类的约束,则只需要配置一个validator即可。
@Configuration
public class ValidatorConfig {
/**
* 配置验证器
*
* @return validator
*/
@Bean
public Validator validator() {
ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
.configure()
// 快速失败模式
.failFast(true)
// .addProperty( "hibernate.validator.fail_fast", "true" )
.buildValidatorFactory();
return validatorFactory.getValidator();
}
}
可以通过方法 failFast(true)或 addProperty("hibernate.validator.fail_fast", "true")设置为快速失败模式,快速失败模式在校验过程中,当遇到第一个不满足条件的参数时就立即返回,不再继续后面参数的校验。否则会一次性校验所有参数,并返回所有不符合要求的错误信息
方法验证配置
如果需要验证方法参数和返回值,则还需要配置另一个对象。
/**
* 设置方法参数验证器
*/
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
MethodValidationPostProcessor postProcessor = new MethodValidationPostProcessor();
// 设置validator模式为快速失败返回
postProcessor.setValidator(validator());
return postProcessor;
}
XML方式
同样的,在XML中也只需要配置这两个对象即可完成配置。
基本配置
<!-- validator基本配置 -->
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="providerClass" value="org.hibernate.validator.HibernateValidator"/>
<!-- 映射资源文件 -->
<property name="validationMessageSource" ref="messageSource"/>
</bean>
<mvc:annotation-driven validator="validator"/>
方法验证配置
<!-- 设置方法参数验证器 -->
<bean id="methodValidationPostProcessor" class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor">
<property name="validator" ref="validator"/>
</bean>
使用
在配置好之后,只要是在spring容器中的对象,都可以让spring自动完成验证。
请求接口验证对象
@RestController
public class UserController {
@PostMapping("/login1")
public void login1(@Valid @RequestBody User user){
//如果有错误会直接抛出。
//...
}
@PostMapping("/login2")
public void login1(@Valid @RequestBody User user, BindingResult result){
// 在控制器内本地处理验证错误
if (result.hasErrors()) {
result.getAllErrors().forEach(s -> System.out.println(s.getDefaultMessage()));
return R.fail(result.getAllErrors().get(0).getDefaultMessage());
}
//...
}
}
一般都是使用第一种方式,然后通过springMVC全局处理异常。
请求验证简单类型
这里必须有两步:
- 必须要配置methodValidationPostProcessor。
- 必须在类上加
@Validated注解。
例:
@Validated
@RestController
public class UserController {
@PostMapping("/login2")
public void checkEmail(@Email String email){
//...
}
}
注解分类
validator-api-2.0的约束注解有22个,具体我们看下面表格
空与非空检查
| 注解 | 支持Java类型 | 说明 |
|---|---|---|
| @Null | Object | 为null |
| @NotNull | Object | 不为null |
| @NotBlank | CharSequence | 不为null,且必须有一个非空格字符 |
| @NotEmpty | CharSequence、Collection、Map、Array | 不为null,且不为空(length/size>0) |
Boolean值检查
| 注解 | 支持Java类型 | 说明 | 备注 |
|---|---|---|---|
| @AssertTrue | boolean、Boolean | 为true | 为null有效 |
| @AssertFalse | boolean、Boolean | 为false | 为null有效 |
日期检查
| 注解 | 支持Java类型 | 说明 | 备注 |
|---|---|---|---|
| @Future | Date、Calendar、Instant、LocalDate、LocalDateTime、LocalTime、MonthDay、OffsetDateTime、OffsetTime、Year、YearMonth、ZonedDateTime、HijrahDate、JapaneseDate、MinguoDate、ThaiBuddhistDate | 验证日期为当前时间之后 | 为null有效 |
| @FutureOrPresent | Date、Calendar、Instant、LocalDate、LocalDateTime、LocalTime、MonthDay、OffsetDateTime、OffsetTime、Year、YearMonth、ZonedDateTime、HijrahDate、JapaneseDate、MinguoDate、ThaiBuddhistDate | 验证日期为当前时间或之后 | 为null有效 |
| @Past | Date、Calendar、Instant、LocalDate、LocalDateTime、LocalTime、MonthDay、OffsetDateTime、OffsetTime、Year、YearMonth、ZonedDateTime、HijrahDate、JapaneseDate、MinguoDate、ThaiBuddhistDate | 验证日期为当前时间之前 | 为null有效 |
| @PastOrPresent | Date、Calendar、Instant、LocalDate、LocalDateTime、LocalTime、MonthDay、OffsetDateTime、OffsetTime、Year、YearMonth、ZonedDateTime、HijrahDate、JapaneseDate、MinguoDate、ThaiBuddhistDate | 验证日期为当前时间或之前 | 为null有效 |
数值检查
| 注解 | 支持Java类型 | 说明 | 备注 |
|---|---|---|---|
| @Max | BigDecimal、BigInteger,byte、short、int、long以及包装类 | 小于或等于 | 为null有效 |
| @Min | BigDecimal、BigInteger,byte、short、int、long以及包装类 | 大于或等于 | 为null有效 |
| @DecimalMax | BigDecimal、BigInteger、CharSequence,byte、short、int、long以及包装类 | 小于或等于 | 为null有效 |
| @DecimalMin | BigDecimal、BigInteger、CharSequence,byte、short、int、long以及包装类 | 大于或等于 | 为null有效 |
| @Negative | BigDecimal、BigInteger,byte、short、int、long、float、double以及包装类 | 负数 | 为null有效,0无效 |
| @NegativeOrZero | BigDecimal、BigInteger,byte、short、int、long、float、double以及包装类 | 负数或零 | 为null有效 |
| @Positive | BigDecimal、BigInteger,byte、short、int、long、float、double以及包装类 | 正数 | 为null有效,0无效 |
| @PositiveOrZero | BigDecimal、BigInteger,byte、short、int、long、float、double以及包装类 | 正数或零 | 为null有效 |
| @Digits(integer = 3, fraction = 2) | BigDecimal、BigInteger、CharSequence,byte、short、int、long以及包装类 | 整数位数和小数位数上限 | 为null有效 |
其他
| 注解 | 支持Java类型 | 说明 | 备注 |
|---|---|---|---|
| @Pattern | CharSequence | 匹配指定的正则表达式 | 为null有效 |
| CharSequence | 邮箱地址 | 为null有效,默认正则 '.*' | |
| @Size | CharSequence、Collection、Map、Array | 大小范围(length/size>0) | 为null有效 |
hibernate-validator扩展约束(部分)
| 注解 | 支持Java类型 | 说明 |
|---|---|---|
| @Length | String | 字符串长度范围 |
| @Range | 数值类型和String | 指定范围 |
| @URL | URL地址验证 |
另外还可以自定义约束注解,此处为简单介绍,就不再赘述。