此篇将大致领略一下Spring的数据校验体系及应用,在此之前你需要提前了解Bean Validation
Spring Validation
spring在spring-context中设计了自己的一套校验体系,位于org.springframework.validation
包下,与Bean Validation类似,核心也是校验器(org.springframework.validation.Validator
)
1 | package org.springframework.validation; |
与Bean Validation校验器不同,spring校验器显得更抽象些,前者通过约束注解+约束验证器组合校验JavaBean,后者更多是泛化了校验过程。spring校验器通过supports方法声明了校验器支持的类型,提供validate方法进行后续的校验,校验结果通过Errors对象回调保存。
就使用场景来说,spring校验器在框架内部应用的比较多,如spring-mvc数据绑定过程、配置bean的数据绑定等。
下面以数据绑定操作为例
定义普通的java bean与其spring校验器,基础的非空等校验可使用ValidationUtils
辅助之。
1 | // ... lombok |
数据绑定操作使用spring校验器
1 | Person person = new Person(); |
显然spring校验器可自定义的能力更宽泛些,校验逻辑实现完全开放。
Spring应用Bean Validation
作为框架来说,spring是要把校验这个基础能力单独封装起来的,引入其它的校验框架时,应该要去适配对接spring的校验体系,达到插件化的效果。故而spring在其Validator的实现中也适配了Bean Validation。
其关键类便是:org.springframework.validation.SmartValidator
与org.springframework.validation.beanvalidation.SpringValidatorAdapter
其中SmartValidator
重载了validate校验方法,提供了分组校验的可能,即validationHints参数,但此处也是以Object参数化处理。
1 | package org.springframework.validation; |
而SpringValidatorAdapter
更是将Bean Validation适配进来,同时实现两个体系的Validator
1 | package org.springframework.validation.beanvalidation; |
继承关系如图
SpringValidatorAdapter
借助Bean Validation的Validator来实现spring的Validator。
因此在前文数据绑定过程中,我们可使用该校验器来使用Bean Validation的能力
此时先给Person类加上约束
1 | // ... lombok |
使用SpringValidatorAdapter作为Person的spring校验器
1 | Person person = new Person(); |
到此Bean Validation与Spring Validation的异同与联系介绍完毕。
Spring 校验功能的应用
显然实际场景中spring更多是借助Bean Validation来“成就”自己的校验体系。
开发者在实际开发中若是使用Bean Validation的原始校验操作,需要编写过于重复的代码,spring肯定不会“坐以待毙”,势必进行了封装。那么在日常开发中,spring的校验体系(能力)是如何激活的呢?
核心便是
org.springframework.validation.annotation.Validated
注解,它便是开启校验的关键开关。
应用于Spring Bean的装配验证
在SpringBoot中,通常我们在设计配置类时,会有一定约束,此时可以借助spring帮我们进行字段的校验,确保配置类bean的有效。只需要在配置类上加以@Validated注解修饰,spring在创建spring bean过程中,bean初始化之际校验该bean。
1 |
|
应用于Spring Bean的方法验证增强
Bean Validation提供了方法参数和返回值验证的能力,spring整合Bean Validation后将这一步骤通过aop来增强实现。
SpringBoot中Validation的自动装配配置
1 | package org.springframework.boot.autoconfigure.validation; |
其中 LocalValidatorFactoryBean便是实际使用的校验器Validator,MethodValidationPostProcessor 负责处理需要增加方法层面校验的bean,通过aop为其提供校验能力。
基本使用
Spring中@Validated
注解是开启校验的关键。
在SpringBean上使用@Validated
修饰,Spring将会对其进行动态代理,以提供校验方法参数和返回值的能力
1 |
|
后续该SpringBean在任一地方使用时,将会校验方法入参与返回值是否满足约束,校验不通过则以ConstraintViolationException异常通知。
分组校验
具有校验能力的SpringBean如何指定校验的组呢?
只需要在@Validated
注解中声明即可
1 | // 声明校验组 |
应用于Spring MVC参数绑定的验证
在web-mvc的controller中,在以下参数绑定场景下可以使用@Validated注解对该参数进行校验。
@RequestBody绑定
通过@RequestBody注解将Request-body的内容(通常是json)绑定到java bean时,可使用@Validated开启校验,由于是在方法参数上开启,所以更能灵活的指定分组,如新增接口使用校验Insert组,更新接口使用Update组。
1
2
3
4
5
6
7
8
public class BeanValidatedController {
public RestResponse handler( { Person person)
// ...
}
}@ModelAttribute绑定
通过@ModelAttribute注解将web视图
org.springframework.ui.Model
中的对象绑定到参数上时,可支持校验1
2
3
4
5
6
7
8
public class BeanValidatedController {
public RestResponse handler( { Person person)
// ...
}
}@RequestPart绑定(非文件类型
MultipartFile
)通过@RequestPart注解将表单中的部分数据绑定到参数上时(如json),可支持校验,
MultipartFile
不支持校验1
2
3
4
5
6
7
8@RestController
@RequestMapping("/api/v2")
public class BeanValidatedController {
@PostMapping("/form")
public RestResponse handler(@Validated @RequestPart Person person) {
// ...
}
}
上述参数绑定场景的校验能力均来自于org.springframework.web.bind.WebDataBinder
,它是web-mvc参数绑定的核心组件。其继承于前文提到的DataBinder
值得注意的是,在web-mvc参数绑定校验中,参数绑定的能力来源于对应的参数解析器HandlerMethodArgumentResolver
,对绑定参数的校验逻辑是其内部解析流程之一,通过源码分析可知,上述几种场景的绑定是支持校验的,激活开关为@Validated注解(实际上@Valid注解也是开关,spring也做了对 @Valid注解的支持)
而诸如通过@RequestParam注解绑定String等基础类型时,是不支持校验的
1 |
|
如果想要支持上述校验,我们需要退回到《Bean Validation应用于Spring Bean的方法验证增强》的功能点上,对controller开启校验的aop代理增强。
改写如下
1 | // 对spring bean 开启校验的aop代理 |
这就意味着当我们对controller加以@Validated 注解修饰时,controller将得到校验的aop代理增强,同时由于web-mvc对参数绑定的校验支持,会同时有两套校验逻辑加在controller上。
如果我们代码如下编写时,即如badHandler方法使用@Valid 修饰参数,会触发两次校验!
1 | // 对spring bean 开启校验的aop代理 |
一次校验是web-mvc参数绑定过程的校验(开关为 @Valid)
触发机制为:controller参数绑定机制 + @Valid修饰参数激活校验开关
此时是spring的校验器在工作;
另外一次是aop代理增强的校验
触发机制为:@Validated开启aop代理 + @Valid修饰参数激活Bean Validation方法级验证
此时是Bean Validation的校验器在工作;
两者的原理大不相同。
故上述代码badHandler方法会触发两次校验,goodHandler仅触发web-mvc参数绑定过程的校验。
到此Spring的数据校验体系的应用与部分原理介绍完毕。