Spring 系列篇之数据处理

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

接口定义

org.springframework.validation.Validator

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) 
// 代码源自MethodValidationPostProcessor
public 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) 

感谢

感谢各位老铁花时间观看!
欢迎留言指正!
内容持续更新!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,718评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,683评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,207评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,755评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,862评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,050评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,136评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,882评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,330评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,651评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,789评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,477评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,135评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,864评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,099评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,598评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,697评论 2 351