Skip to main content

数据验证处理

本篇文章主要讲解Spring 提供的对象合法性验证接口org.springframework.validation.Validator,顺带提及一下javax.validation.Validator,注意本篇文章未加特别说明的Validator一律指org.springframework.validation.Validator

Validator接口说明#

org.springframework.validation.Validator 是Spring 为我们提供了一套验证对象的接口,接口定义如下:

public interface Validator {    boolean supports(Class<?> clazz);    void validate(Object target, Errors errors);}

从定义上我们可以猜到Validator主要就是验证对象是否合法,并将验证结果添加到Errors对象中。多的不解释,先上几行代码!

    // (1) javax.validation.Validator     private final Validator nativeValidator = Validation.buildDefaultValidatorFactory().getValidator();    // (2) SpringValidatorAdapter 对象实现了javax.validation.Validator 和 org.springframework.validation.Validator两接口     private final SpringValidatorAdapter validatorAdapter = new SpringValidatorAdapter(nativeValidator);    //messageSource    private final StaticMessageSource messageSource = new StaticMessageSource();
    @Test  // SPR-13406    public void testNoStringArgumentValue() throws Exception {        // (3) 需要被验证的对象          TestBean testBean = new TestBean();        testBean.setPassword("pass");        testBean.setConfirmPassword("pass");        // (4) Error对象,用于存储验证错误信息          BeanPropertyBindingResult errors = new BeanPropertyBindingResult(testBean, "testBean");        //实际验证方法        validatorAdapter.validate(testBean, errors);        //获取验证结果        assertThat(errors.getFieldErrorCount("password"), is(1));        assertThat(errors.getFieldValue("password"), is("pass"));        FieldError error = errors.getFieldError("password");        assertNotNull(error);        // (5) 利用messageSource获取错误描述信息         assertThat(messageSource.getMessage(error, Locale.ENGLISH), is("Size of Password must be between 8 and 128"));        //检查错误原因        assertTrue(error.contains(ConstraintViolation.class));        //获取具体错误原因对象        assertThat(error.unwrap(ConstraintViolation.class).getPropertyPath().toString(), is("password"));        assertThat(SerializationTestUtils.serializeAndDeserialize(error.toString()), is(error.toString()));    }

以上是org.springframework.validation.beanvalidation2.SpringValidatorAdapterTests单元测试片段代码,我们主要从上面标注的([1,2,3,4,5])来入手了解我们的Spring Validator的功能与原理

(1) javax.validation.Validator#

这是validation-api中的比较重量级的接口,其功能也是验证对象的合法性,Constrain once, validate everywhere这是官网标语。我们在来看一张图 validator support JSR 这张图明确表明了,validation-api版本与JSR对应关系,如果不知道JSR是什么,老铁这里不用纠结,你只需要知道,这里的JSR就是验证对象合法性的一些规范(而实现了这套规范的有hibernate-validator)如下图是一些注解规范: Validate Annotation 这些规范语义很清楚,就不过多解释。那么javax.validation.Validator为什么会出现在这里了?很简单,一句话不要重复造轮子在这里体现,也就是说Spring 的Validator是包含了我们JSR303,JSR349等规范了的,并且还是使用的hibernate-validator实现。

(2) SpringValidatorAdapter#

SpringValidatorAdapter是Validator的一个实现类,我们在看看其类图,可以知道SpringValidatorAdapter是实现了Validatorjavax.validation.Validator两个接口的。 SpringValidatorAdapter

(3) 需要被验证的对象#

我们看看TestBean的定义

@Same(field = "password", comparingField = "confirmPassword")@Same(field = "email", comparingField = "confirmEmail")static class TestBean {
    @Size(min = 8, max = 128)    private String password;
    private String confirmPassword;
    @Pattern(regexp = "[\\w.'-]{1,}@[\\w.'-]{1,}")    private String email;
    @Pattern(regexp = "[\\p{L} -]*", message = "Email required")    private String confirmEmail;}

可以看到这个类已经被很多注解修饰,这里的注解也就是我们需要验证的规则。@Size@Patternvalidator-api中定义的hibernate-validator已经完全实现了验证功能,而@Same是Spring Test自定义的 注解,如下:

@Documented@Constraint(validatedBy = {SameValidator.class})@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})@Retention(RetentionPolicy.RUNTIME)@Repeatable(SameGroup.class)@interface Same {    。。。。}

这里我们重点看@Constraint(validatedBy = {SameValidator.class}) 这就是@Same验证逻辑的实现类,当然这个类必须实现ConstraintValidator接口,以下是接口定义

public interface ConstraintValidator<A extends Annotation, T> {        //注解的元信息    default void initialize(A constraintAnnotation) {    }        //验证方法    boolean isValid(T value, ConstraintValidatorContext context);}

(4) Errors对象,用于存储验证错误信息#

通过下图左边可以看到Errors接口提供了添加错误信息和获取错误信息的接口,这也是Spring为我们提供的统一处理和获取Error信息的接口。而右边就是我们BeanPropertyBindingResult的一个关系类图,也就Errors接口的具体实现类。 Errors

(5) 利用messageSource获取错误描述信息#

这里很和谐的和Spring的MessageResource搭配使用。以下是DefaultMessageCodesResolver.resolveMessageCodes方法为生成code的规则。 code生成规则

总结#

现在我们在对Spring Validator 的功能做次总结

  • 支持 hibernate-validate 实现的 validator-api
  • 对错误信息提供了友好的封装
  • 错误信息能够无缝结合MessageSource,以便提供国际化支持

使用#

Service 中使用#

@Service@Validated  //Validated 注意 一定要在类或接口上 因为切面是用的 //Pointcut pointcut = new AnnotationMatchingPointcut(this.validatedAnnotationType, true) // 代码源自MethodValidationPostProcessorpublic class HelloService {
   //参数使用 Valid    public void say(@Valid Hello hello) {        System.out.println("xxx");    }}

Controller 中使用#

@RequestMapping("say")//此处使用Validated 或者 Valid 都可以//ModelAttributeMethodProcessor.determineValidationHints 判断 Validated 或者 Valid //使用Errors 可以接收错误信息,不用Errors会直接抛异常到前端public String say(@Validated Hello hehe, Errors errors) 

无论是在service,还是controller中的使用,spring 最终都是用 hibernateValidatorImpl 做的校验