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

 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)的源码如下:

    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.mfbz.cn/a/576130.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

使用CNN实现新闻文本分类

一、实验目的&#xff1a; 理解卷积神经网络的基本概念和原理&#xff1b;了解卷积神经网络处理文本数据的基本方法&#xff1b;掌握卷积神经网络处理文本数据的实践方法&#xff0c;并实现新闻文本的分类任务。 实验要求&#xff1a; 使用Keras框架定义并训练卷积神经网络模…

防盗链在nginx中如何配置,简单演示403forbidden的效果

一、使用场景&#xff1a; 资源被其他网站无端盗用 服务器压力无端增加 二、实现方法 1.valid_referers指令可以检测被访问资源从哪个地址来 2.通过referer头字段判断 3.若为空&#xff0c;报403错误 nginx的准备工作&#xff1a; 可以看 虚拟机中使用LNMP模拟跨域并结合…

书籍推推荐之二--《生命的色彩》

史钧《生命的色彩》 在生活中&#xff0c;我们会注意到一个有趣的现象&#xff1a;每个人的头发颜色各不相同&#xff0c;有黑色、灰色、黄色、棕红色、银白色等&#xff0c;但就是没有绿色。对于生活在丛林中的早期人类来说&#xff0c;绿色的头发简直就是天然的迷彩服&#x…

基于arcpro3.0.2版的使用深度学习检测对象之椰子树

基于arcpro3.0.2版的使用深度学习检测对象之椰子树 GPU显卡Nivda 1080 训练模型图 (四)检测对象之椰子树 使用深度学习检测对象 打开 detect objects using deep learning,参数 输入栅格为要检测的影像 模型定位为上一步输出的.emd文件 cpu模式Max Overlap Ratio0.4 运行时间…

C++感受6-Hello World 交互版

变量、常量输入、输出、流getline() 函数读入整行输入Hello() 函数复习新定义函数 Input() 实现友好的人机交互还有 “痘痘” 为什么挤不到的分析…… 1. DRY 原则简介 上一节课&#xff0c;我们写了两版“问候”程序。第一版的最大问题是重复的内容比较多&#xff0c;每一次问…

今日arXiv最热大模型论文:大模型也来看球,还能判断是否犯规

在足球世界&#xff0c;裁判的哨声可谓“千金难买”&#xff0c;因为它能直接决定俱乐部的钱包是鼓是瘪。但球场变化莫测&#xff0c;非常考验裁判的水平。 2022年卡塔尔世界杯上&#xff0c;半自动越位识别技术&#xff08;SAOT&#xff09;闪亮登场&#xff0c;通过12台摄像…

高并发场景中DB和Cache的一致性新的方案感想

拜读了: 美团2面&#xff1a;如何保障 MySQL 和 Redis 数据一致性&#xff1f;这样答&#xff0c;虐爆面试官这篇文章后的感想 高并发场景中数据库和缓存的一致性和可用性的感想 1&#xff0c;先更新缓存&#xff0c;再更新数据库1.1&#xff0c;前提1.2&#xff0c;理由1.2.1&…

Echarts-知识图谱

Echarts-知识图谱 demo地址 打开CodePen 效果 思路 1. 生成根节点 2. 根据子节点距离与根节点的角度关系&#xff0c;生成子节点坐标&#xff0c;进而生成子节点 3. 从子节点上按角度生成对应的子节点 4. 递归将根节点与每一层级子节点连线核心代码 定义节点配置 functio…

目标检测——大规模商品数据集

引言 亲爱的读者们&#xff0c;您是否在寻找某个特定的数据集&#xff0c;用于研究或项目实践&#xff1f;欢迎您在评论区留言&#xff0c;或者通过公众号私信告诉我&#xff0c;您想要的数据集的类型主题。小编会竭尽全力为您寻找&#xff0c;并在找到后第一时间与您分享。 …

阿里云企业邮箱API的使用方法?调用限制?

阿里云企业邮箱API性能如何优化&#xff1f;配置邮箱API的优势&#xff1f; 阿里云企业邮箱以其稳定、高效和安全的特点&#xff0c;受到了众多企业的青睐。而阿里云企业邮箱API的开放&#xff0c;更是为企业提供了更加灵活、便捷的管理和操作方式。下面&#xff0c;我AokSend…

新标准日本语初下 课后练习作业

新版标准日本语初下 第二十五課 これは明日会議で使う資料です 第二十五課 これは明日会議で使う資料です &#xff12;&#xff14;&#xff0d;&#xff10;&#xff14;&#xff0d;&#xff12;&#xff16; 練習&#xff12;&#xff15;&#xff0d;1&#xff0d;1 例…

uniapp中vue写微信小程序的生命周期差别

根据uniapp官网里的生命周期&#xff0c;感觉不太对劲&#xff0c;就自己测试了几个&#xff0c;发现有所差别。 红字数字 为 实际测试生命周期顺序。 因为需要页面传参 后再 初始化数据&#xff0c;而onLoad(option)接收参数后&#xff0c;就已经过了create()了&#xff0c;所…

绘制等值线地图——以气压等压线为例(Python版)

绘制等值线地图——以气压等压线为例(Python版&#xff09; - 知乎 1.前期环境配置 本篇博客主要使用了basemap和netCDF4两个库&#xff0c;推荐使用conda新建虚拟环境并安装相关的包 可以参考笔者之前的博客1和博客2完成windows系统上conda的安装在conda完成安装后在anacond…

【牛客网】:链表的回文结构(提升)

&#x1f381;个人主页&#xff1a;我们的五年 &#x1f50d;系列专栏&#xff1a;每日一练 &#x1f337;追光的人&#xff0c;终会万丈光芒 目录 &#x1f3dd;问题描述&#xff1a; &#x1f3dd;问题分析&#xff1a; 步骤一&#xff1a;查找链表的中间节点 步骤二&am…

C++教学——从入门到精通 11.嵌套循环及数组

上次讲到了循环&#xff0c;这次来讲嵌套循环 如果一个人叫你用C来画一个10*10/2cm^2三角形会么&#xff1f; 这就要用到嵌套循环了 来看看结构&#xff1a; for(变量类型1 变量;条件1;返回值1){语句1;for(变量类型 变量2;条件2;返回值2){语句2;}语句3; } 语句1,2,3是依次…

ThingsBoard远程RPC调用设备

使用 RPC 功能 客户端 RPC 从设备发送客户端 RPC 平台处理客户端RPC 服务器端 RPC 服务器端RPC结构 发送服务器端RPC 使用 RPC 功能 ThingsBoard 允许您从服务器端应用程序向设备发送远程过程调用 (RPC)&#xff0c;反之亦然。基本上&#xff0c;此功能允许您向设备发送命…

关于discuz论坛网址优化的一些记录(伪静态)

最近网站刚上线&#xff0c;针对SEO做了些操作&#xff0c;为了方便网站网页被收录&#xff0c;特此记录下 1.开启伪静态 按照操作勾选所有项&#xff0c;然后点击查看伪静态规则 2.打开宝塔&#xff0c;找到左侧列表的网站&#xff0c;然后找到相应站点的设置。把discuz自动…

科普:嵌入式代码软件在环(SiL)测试的可靠性

关键词&#xff1a;嵌入式系统、软件在环&#xff08;SiL&#xff09;、测试、生命周期 01.简介 当前&#xff0c;嵌入式系统开发的大趋势为通过软件实现大量的硬件功能&#xff0c;这导致软件的复杂程度显著上升——代码开发成本和风险也成倍增加。复用已有系统中的软件组件…

【数据结构(邓俊辉)学习笔记】绪论05——动态规划

文章目录 0.前言1. Fibonacci数应用1.1 fib&#xff08;&#xff09;&#xff1a;递归1.1.1 问题与代码1.1.2 复杂度分析1.1.3 递归分析 1.2 fib&#xff08;&#xff09;&#xff1a;迭代 0.前言 make it work,make it right,make it fast. 让代码能够不仅正确而且足够高效地…

明日周刊-第7期

转眼间就又快到了五一假期&#xff0c;小长假有什么计划吗。封面配图是杭州高架上的月季花&#xff0c;非常好看。 文章目录 一周热点资源分享言论歌曲推荐 一周热点 鸿蒙系统持续扩大影响力&#xff1a;近期&#xff0c;华为官方宣布广东省已有超过600款应用加入鸿蒙系统&…
最新文章