【源码】Spring validation参数校验实现原理总结

devtools/2024/9/24 5:52:38/

 Spring validation参数校验系列

1、Spring validation参数校验基本使用

2、Spring validation参数校验之自定义校验规则及编程式校验等进阶篇

3、【源码】Spring validation参数校验原理解析之Controller控制器参数校验中@RequestBody参数校验实现原理

4、【源码】Spring validation参数校验原理解析之Controller控制器参数校验中@ModelAttribute及实体类参数校验实现原理

5、【源码】Spring validation参数校验原理解析之基本类型参数及Service层方法参数校验实现原理

6、【源码】Spring validation校验的核心类ValidatorImpl、MetaDataProvider和AnnotationMetaDataProvider源码分析

7、Spring validation参数校验高级篇之跨参数校验Cross-Parameter及分组序列校验@GroupSequenceProvider、@GroupSequence

8、【源码】Spring validation参数校验之跨参数校验Cross-Parameter原理分析

9、【源码】Spring validation参数校验之分组序列校验@GroupSequenceProvider、@GroupSequence的实现原理

10、【源码】Spring validation参数校验实现原理总结

前言

Spring validation的校验核心部分采用的是Hibernate validation,而Hibernate validation的设计比较复杂,《Spring validation参数校验系列》文章用了六篇来讲解Spring/Hibernate validation的源码。由于篇幅限制,要一次性全部分析清楚很困难,每一篇只在尽量保证讲清楚对应篇章核心内容的前提下,穿插引入一些细节。在这一篇中,准备对源码做一个总结,从整体的角度来熟悉一下Sprign validation参数校验的整体结构。

一、参数约束解析

通过BeanMetaDataManager.getBeanMetaData(Class<T> beanClass)获得beanClass添加的约束元数据BeanMetaDataImpl对象。

BeanMetaDataManager的实现类为BeanMetaDataManagerImpl。在BeanMetaDataManagerImpl中,维护一个ConcurrentReferenceHashMap<Class<?>, BeanMetaData<?>> beanMetaDataCache属性,缓存某个beanClass的约束元数据。如果没在缓存中,则调用createBeanMetaData(Class<T> clazz),创建解析创建约束元数据。

BeanMetaDataManagerImpl.createBeanMetaData(Class<T> clazz)的源码如下:

java">    private <T> BeanMetaDataImpl<T> createBeanMetaData(Class<T> clazz) {BeanMetaDataBuilder<T> builder = BeanMetaDataBuilder.getInstance(constraintCreationContext, executableHelper, parameterNameProvider,validationOrderGenerator, clazz, methodValidationConfiguration );for ( MetaDataProvider provider : metaDataProviders ) {// getBeanConfigurationForHierarchy()方法遍历beanClass及其父类,调用AnnotaionMetaDataProvider.getBeanConfiguration()方法// 获取对应类添加的约束注解,封装成BeanConfiguration对象for ( BeanConfiguration<? super T> beanConfiguration : getBeanConfigurationForHierarchy( provider, clazz ) ) {// 在BeanMetaDataBuilder中添加BeanConfiguration对象// BeanMetaDataBuilder.add()// 1、获取并遍历约束元素,获取beanConfiguration中的sequenceSource// 和defaultGroupSequence保存到builder中// 2、获取并遍历约束元素,执行addMetaDataToBuilder()方法 -> addMetaDataToBuilder()//【执行methodBuilder.add( constrainedElement )】 -> ExecutableMetaData.Builder.add(),// 在该方法中,执行constrainedExecutable.getCrossParameterConstraints(),获取跨参数校验约束。加入到Builder中builder.add( beanConfiguration );}}// 将类中添加的约束信息封装成BeanMetaDataImpl对象。// BeanMetaDataBuilder.build()【遍历builders,执行builder.build()】 -> // BuilderDelegate.build()【执行methodBuilder.build()】 -> ExecutableMetaData.build()【new一个ExecutableMetaData对象。// 调用adaptOriginsAndImplicitGroups( crossParameterConstraints )对约束进行适配修改,获得跨参数约束集合,// 保存在crossParameterConstraints属性中】return builder.build();}

1.1 创建一个BeanMetaDataBuilder<T> builder对象;

1.2 遍历metaDataProviders集合。MetaDataProvider为约束元数据的提供器,解析beanClass添加的约束。Spring中有三个提供器,分别为AnnotationMetaDataProvider、ProgrammaticMetaDataProvider、XmlMetaDataProvider,分别处理通过注解、程序添加、XML配置三种来源的约束信息。详见

【源码】Spring validation校验的核心类ValidatorImpl、MetaDataProvider和AnnotationMetaDataProvider源码分析-CSDN博客

本系列只讲解了AnnotationMetaDataProvider,即通过注解添加的约束的校验。

1.2.1 调用getBeanConfigurationForHierarchy()方法遍历beanClass及其父类,调用AnnotaionMetaDataProvider.getBeanConfiguration(),解析类中添加的约束信息,并封装成BeanConfiguration对象,记录了bean的属性、类、参数、方法添加的约束信息、配置来源、分组序列以及动态分组序列程序。详见

【源码】Spring validation校验的核心类ValidatorImpl、MetaDataProvider和AnnotationMetaDataProvider源码分析-CSDN博客

【源码】Spring validation参数校验之跨参数校验Cross-Parameter实现原理分析-CSDN博客

1.2.2 执行builder.add(beanConfiguration),将beanConfiguration添加到builder中。

1.2.2.1 获取并遍历约束元素,获取beanConfiguration中的sequenceSource和defaultGroupSequence保存到builder中。

1.2.2.2 获取并遍历约束元素,执行addMetaDataToBuilder()方法【执行methodBuilder.add( constrainedElement )】 -> ExecutableMetaData.Builder.add(),在该方法中,执行constrainedExecutable.getCrossParameterConstraints(),获取跨参数校验约束。加入到Builder中

1.3 执行BeanMetaDataBuilder.build(),将类中添加的约束信息封装成BeanMetaDataImpl对象。

1.3.1 BeanMetaDataBuilder.build()【遍历builders,执行builder.build()】 -> BuilderDelegate.build()【执行methodBuilder.build()】 -> ExecutableMetaData.build()【new一个ExecutableMetaData对象。调用adaptOriginsAndImplicitGroups( crossParameterConstraints )对约束进行适配修改,获得跨参数约束集合,保存在crossParameterConstraints属性中】

1.3.2 new一个BeanMetaDataImpl对象。在BeanMetaDataImpl的构造方法中,对约束元数据进行分类,对分组系列及动态分组系列程序进行解析处理。详见

【源码】Spring validation参数校验之分组序列校验@GroupSequenceProvider、@GroupSequence的实现原理-CSDN博客

二、参数校验

2.1 Spring MVC非基础类型参数校验入口

Spring MVC的方法参数处理解析器HandlerMethodArgumentResolver的resolveArgument()方法用于自动向Controller类的接口方法参数注入值(自动为方法参数赋值),解析完参数之后,最终调用ValidationImpl的校验方法,如果参数中有添加了@Validated或@Valid注解,进行参数校验。

针对不同的参数类型,使用不同的HandlerMethodArgumentResolver。详见

【源码】Spring validation参数校验原理解析之Controller控制器参数校验中@ModelAttribute及实体类参数校验实现原理_validation 在controller层如何校验-CSDN博客

2.2 Service层或Controller层中的基础类型参数校验入口

Controller层中的基础类型参数的解析器为RequestParamMethodArgumentResolver,在该类的resolveArgument()方法中,并不会进行参数校验。

在SpringBoot的WebMvcAutoConfiguration自动配置类中自动装载ValidationAutoConfiguration类,类中创建一个MethodValidationPostProcessor对象,类型为FilteredMethodValidationPostProcessor。

在MethodValidationPostProcessor中,添加了@Validated注解的切点以及针对该切点的Advisor增强器。只要在类中添加了@Validated注解,对应类就会被增强,对应方法在执行之前,都会先执行MethodValidationInterceptor的invoke()方法。在invoke()方法中,调用validateParameters()进行参数校验。说明:针对Service层或Constroller中的基本类型参数校验时,类需要添加@Validated注解。详见

【源码】Spring validation参数校验原理解析之基本类型参数及Service层方法参数校验实现原理_service层校验实体类-CSDN博客

2.3 参数校验

Spring validation参数校验的底层采用的是Hibernate validation进行的校验。ValidatorImpl是Hibernate validation参数校验Validator接口的唯一实现。ValidatorImpl类中提供了对实体、实体中的属性、实体中的方法、实体的构造方法、实体的方法返回值的参数验证。详见

【源码】Spring validation校验的核心类ValidatorImpl、MetaDataProvider和AnnotationMetaDataProvider源码分析-CSDN博客

2.3.1 校验之前,会先通过@Validated注解获取校验分组;

2.3.2 通过BeanMetaDataManager.getBeanMetaData(beanClass)获取beanClass中添加的约束元数据信息BeanMetaDataImpl对象;

2.3.3 调用determineGroupValidationOrder(Class<?>[] groups),其中的groups为validate()方法中传入的,为@Validated注解中添加的,默认为Default分组。该方法调用validationOrderGenerator.getValidationOrder( resultGroups )获取分组顺序。详见

【源码】Spring validation参数校验之分组序列校验@GroupSequenceProvider、@GroupSequence的实现原理-CSDN博客

2.3.4 执行validateInContext(BaseBeanValidationContext<T> validationContext, BeanValueContext<U, Object> valueContext, ValidationOrder validationOrder),进行参数校验;

2.3.4.1 判断是否添加了分组序列,如果有,且有动态分组系列程序DefaultGroupSequenceProvider,则会再次执行DefaultGroupSequenceProvider.getValidationGroups()方法,动态获取分组;

2.3.4.2 遍历分组,调用validateConstraintsForCurrentGroup(BaseBeanValidationContext<?> validationContext, BeanValueContext<?, Object> valueContext)执行该分组的校验、调用validateCascadedConstraints(BaseBeanValidationContext<?> validationContext, ValueContext<?, Object> valueContext)执行级联校验;

2.3.4.3 如果有动态分组,遍历分组,调用validateConstraintsForCurrentGroup(BaseBeanValidationContext<?> validationContext, BeanValueContext<?, Object> valueContext)执行该分组的校验;

2.3.4.4 在validateConstraintsForCurrentGroup(BaseBeanValidationContext<?> validationContext, BeanValueContext<?, Object> valueContext)执行校验时,最终会遍历每个约束,执行validateMetaConstraint(BaseBeanValidationContext<?> validationContext, ValueContext<?, Object> valueContext, Object parent, MetaConstraint<?> metaConstraint)完成真正的校验;

2.3.4.4.1 执行valueContext.setCurrentValidatedValue(valueContext.getValue(parent, metaConstraint.getLocation())),通过MetaConstraint中的location获取传入约束校验器ConstraintValidator.isValid()方法的参数值;

2.3.4.4.2 执行ConstraintTree.getInitializedConstraintValidator()获取对应约束的ConstraintValidator对象,首次获取会调用初始化方法;

2.3.4.4.3 执行ConstraintTree.validateSingleConstraint()方法,调用ConstraintValidator的isValid()方法进行参数校验;

2.3.4.4.4 如果有设置了快速失败,则只要其中一个校验失败,校验就结束;

详见

【源码】Spring validation参数校验之分组序列校验@GroupSequenceProvider、@GroupSequence的实现原理-CSDN博客

【源码】Spring validation参数校验原理解析之Controller控制器参数校验中@RequestBody参数校验实现原理_simpleconstrainttree-CSDN博客

结尾

《Spring validation参数校验系列》文章花了很多篇幅从源码的角度讲解Spring validation的实现,由于Hibernate validation设计比较复杂,本人才疏学浅,许多细节也没有全部研究透。

关于本篇内容你有什么自己的想法或独到见解,欢迎在评论区一起交流探讨下吧。


http://www.ppmy.cn/devtools/23988.html

相关文章

c++高级篇(三) ——Linux下IO多路复用之poll模型

poll模型 前言 poll模型与select的实现原理相近&#xff0c;所以绝大数的原理其实可以参考select&#xff0c;我们这里对二者的相同点不做过多探究&#xff0c;如果有需要可以去看一下博主的上一篇文章&#xff1a; c高级篇(二) ——Linux下IO多路复用之select模型 这里我们只…

Jmeter插件技术:性能测试中服务端资源监控

性能测试过程中我们需要不断的监测服务端资源的使用情况&#xff0c;例如CPU、内存、I/O等。 Jmeter的插件技术可以很好的实时监控到服务器资源的运行情况&#xff0c;并以图形化的方式展示出来&#xff0c;非常方便我们性能测试分析。 操作步骤&#xff1a; 1、安装插件管理…

封装形式,进化,DIP封装及键出方法

本文主要讨论芯片封装的主要形式&#xff0c;概念&#xff0c;以及芯片封装的演化&#xff0c;最后以DIP封装为例&#xff0c;分析键出方式。 1-IC封装的形式 IC 封装是指将组成电子器件的各个组成部分&#xff0c;包括半导体芯片、基板、管脚连接线等&#xff0c;按照要求布局…

力扣(leetcode) 407. 接雨水 II 3D接雨水

力扣(leetcode) 407. 接雨水 II 3D接雨水 给你一个 m x n 的矩阵&#xff0c;其中的值均为非负整数&#xff0c;代表二维高度图每个单元的高度&#xff0c;请计算图中形状最多能接多少体积的雨水。 示例 1: 输入: heightMap [[1,4,3,1,3,2],[3,2,1,3,2,4],[2,3,3,2,3,1]] 输…

【C语言】文件操作

1、为什么使用文件操作&#xff1f; 如果没有⽂件&#xff0c;我们写的程序的数据是存储在电脑的内存中&#xff0c;如果程序退出&#xff0c;内存回收&#xff0c;数据就丢失了&#xff0c;等再次运⾏程序&#xff0c;是看不到上次程序的数据的&#xff0c;如果要将数据进⾏持…

手撕C语言题典——移除元素(顺序表)

搭配使用更佳哦~~ 数据结构之顺顺顺——顺序表-CSDN博客 数据结构之顺序表的基本操作-CSDN博客 前面学了顺序表的相关知识&#xff0c;我们来尝试做一下关于顺序表的经典算法题~ 前言 27. 移除元素 - 力扣&#xff08;LeetCode&#xff09; 移除元素作为力扣上的一道不算太难…

微信小程序+esp8266温湿度读取

本文主要使用微信小程序显示ESP8266读取的温湿度并通过微信小程序控制LED灯。小程序界面如下图所示 原理讲解 esp8266 通过mqtt发布消息,微信小程序通过mqtt 订阅消息,小程序订阅后,就可以实时收到esp8266 传输来的消息。 个人可免费注册五个微信小程序账号,在微信小程序官…

Baidu comate智能编程助手评测

Baidu comate智能编程助手评测 作者&#xff1a;知孤云出岫 目录 一&#xff0e; 关于comate产品 二&#xff0e; 关于comate产品体验 三&#xff0e; 关于实际案例. 四&#xff0e; 关于baidu comate编程助手的实测体验感悟 五&#xff0e; …