🔥一文看懂hibernate validator基本用法

651 阅读6分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 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全局处理异常。

请求验证简单类型

这里必须有两步:

  1. 必须要配置methodValidationPostProcessor
  2. 必须在类上加@Validated注解。

例:

@Validated
@RestController
public class UserController {
    
    @PostMapping("/login2")
    public void checkEmail(@Email String email){
        //...
    }
}

注解分类

validator-api-2.0的约束注解有22个,具体我们看下面表格

空与非空检查

注解支持Java类型说明
@NullObject为null
@NotNullObject不为null
@NotBlankCharSequence不为null,且必须有一个非空格字符
@NotEmptyCharSequence、Collection、Map、Array不为null,且不为空(length/size>0)

Boolean值检查

注解支持Java类型说明备注
@AssertTrueboolean、Boolean为true为null有效
@AssertFalseboolean、Boolean为false为null有效

日期检查

注解支持Java类型说明备注
@FutureDate、Calendar、Instant、LocalDate、LocalDateTime、LocalTime、MonthDay、OffsetDateTime、OffsetTime、Year、YearMonth、ZonedDateTime、HijrahDate、JapaneseDate、MinguoDate、ThaiBuddhistDate验证日期为当前时间之后为null有效
@FutureOrPresentDate、Calendar、Instant、LocalDate、LocalDateTime、LocalTime、MonthDay、OffsetDateTime、OffsetTime、Year、YearMonth、ZonedDateTime、HijrahDate、JapaneseDate、MinguoDate、ThaiBuddhistDate验证日期为当前时间或之后为null有效
@PastDate、Calendar、Instant、LocalDate、LocalDateTime、LocalTime、MonthDay、OffsetDateTime、OffsetTime、Year、YearMonth、ZonedDateTime、HijrahDate、JapaneseDate、MinguoDate、ThaiBuddhistDate验证日期为当前时间之前为null有效
@PastOrPresentDate、Calendar、Instant、LocalDate、LocalDateTime、LocalTime、MonthDay、OffsetDateTime、OffsetTime、Year、YearMonth、ZonedDateTime、HijrahDate、JapaneseDate、MinguoDate、ThaiBuddhistDate验证日期为当前时间或之前为null有效

数值检查

注解支持Java类型说明备注
@MaxBigDecimal、BigInteger,byte、short、int、long以及包装类小于或等于为null有效
@MinBigDecimal、BigInteger,byte、short、int、long以及包装类大于或等于为null有效
@DecimalMaxBigDecimal、BigInteger、CharSequence,byte、short、int、long以及包装类小于或等于为null有效
@DecimalMinBigDecimal、BigInteger、CharSequence,byte、short、int、long以及包装类大于或等于为null有效
@NegativeBigDecimal、BigInteger,byte、short、int、long、float、double以及包装类负数为null有效,0无效
@NegativeOrZeroBigDecimal、BigInteger,byte、short、int、long、float、double以及包装类负数或零为null有效
@PositiveBigDecimal、BigInteger,byte、short、int、long、float、double以及包装类正数为null有效,0无效
@PositiveOrZeroBigDecimal、BigInteger,byte、short、int、long、float、double以及包装类正数或零为null有效
@Digits(integer = 3, fraction = 2)BigDecimal、BigInteger、CharSequence,byte、short、int、long以及包装类整数位数和小数位数上限为null有效

其他

注解支持Java类型说明备注
@PatternCharSequence匹配指定的正则表达式为null有效
@EmailCharSequence邮箱地址为null有效,默认正则 '.*'
@SizeCharSequence、Collection、Map、Array大小范围(length/size>0)为null有效

hibernate-validator扩展约束(部分)

注解支持Java类型说明
@LengthString字符串长度范围
@Range数值类型和String指定范围
@URLURL地址验证

另外还可以自定义约束注解,此处为简单介绍,就不再赘述。