SpringMVC源码深度解析(中)
接上一遍博客《SpringMVC源码深度解析(上)》继续聊。最后聊到了SpringMVC的九大组建的初始化,以 HandlerMapping为例,SpringMVC提供了三个实现了,分别是:BeanNameUrlHandlerMapping、RequestMappingHandlerMapping、RouterFunctionMapping,其中最常用的就是 RequestMappingHandlerMapping,与@RequestMapping注解相关,以它为例,先看看类的继承图:

可知该类实现了InitializingBean接口,这是Spring的扩展点,Bean对象在初始化的时候,会调用 RequestMappingHandlerMapping#afterPropertiesSet()方法。如果使用@EnableWebMvc注解或者手动往Spring容器中注入 RequestMappingHandlerMapping对象,Web容器在调用refresh()方法的过程中,会调用afterPropertiesSet()方法,如果不是通过以上两种方式的话,由上一篇博客 《SpringMVC源码深度解析(上)》可知,会读取dispatcherservlet.properties文件,获取默认的HandlerMapping的实现类,但是由于在前面已经调用了Web容器的refresh()方法,完成了容器的刷新,因此,这里需要特殊处理,代码如下:



可知,这里会显示的调用AutowireCapableBeanFactory#createBean()方法,才会经历Bean的实例化与初始化,才会调用 InitializingBean#afterPropertiesSet()方法。我专门提到这个方法,也侧面说明这个方法很重要,看看这个方法,代码如下:




重点看看AbstractHandlerMethodMapping#getMappingForMethod()方法,代码如下:


再看看AbstractHandlerMethodMapping#registerHandlerMethod()方法,代码如下:



到这里为止就可以知道:RequestMappingHandlerMapping在初始化的时候,就会获取到所有被@Controller/@RequestMapping注解修饰的类,并且得到该类中所有被@Requestmapping注解修饰的方法,每个被@RequestMapping注解修饰的方法,最终生成一个对应的HandlerMethod对象(存储被@Controller/@RequestMapping注解修饰的对象和被@RequestMapping注解修饰的方法),并存储在内部类RequestMappingHandlerMapping.MappingRegistry的registry(Map)属性中,key为RequestMappingInfo对象,value为MappingRegistration对象(存储HandlerMethod对象)。
聊完了RequestMappingHandlerMapping在初始化,再聊聊SpringMVC是如何处理请求。其实就是要搞清楚DispatcherServlet的原理,它是一个Servlet,在《SpringMVC源码深度解析(上)》中也说到了,创建了DispatcherServlet对象后,会添加到Servlet容器中。如果有请求来了,会解析url并匹配,如果是符合DispatcherServlet的匹配路径,则调用 GenericServlet#service()方法,这是一个抽象方法,因此实际调用的是HttpServlet#service()方法,代码如下:


以POST请求为例,则调用的是HttpServlet#doPost()方法,代码如下:

其实对于DispatcherServlet来讲,不管是哪种请求,最终都是调用FrameworkServlet#processRequest()方法,代码如下:



DispatcherServlet#doDispatch()方法是处理请求的核心,会重点讲,代码如下:





在AbstractHandlerMethodMapping#lookupHandlerMethod()方法中,主要是进行路径的匹配,由于在前面RequestMappingInfoHandlerMapping初始化的时候,已经存储好了所有的匹配路径及其HandlerMethod,存放在 RequestMappingInfoHandlerMapping.MappingRegistry的pathLookup属性中,因此如果满足匹配要求,是可以获取到HandlerMethod对象的,这块就说了,涉及到匹配逻辑,比较复杂,感兴趣可以自己研究。回到AbstractHandlerMapping#getHandler()方法中,看看AbstractHandlerMapping#getHandlerExecutionChain()方法,代码如下:

可知这这个方法中,就是创建HandlerExecutionChain对象,并将HandlerInterceptor设置到HandlerExecutionChain对象的interceptorList属性中。再回到DispatcherServlet#doDispatch()方法中,代码如下:




因此返回的HandlerAdapter对象是RequestMappingHandlerAdapter,这里当用到的是适配器设计模式,为什么要做适配呢?还记得之前HandlerMapping由三个实现类的,实际上处理的是三种不同的Controller,因此需要有对应的适配器处理不同的情况,看看HandlerAdapter#handle()方法就知道了,具体如下:




主要是处理这三种情况:
① 一种是被@Controller/@RestController/@RequestMapping注解修饰的类;
② 实现了HttpRequestHandler接口的类;
③ 实现了Controller接口的类;
这三种情况的类,都属于Controller,因此需要不同的适配器处理,我主要讲①,剩下的两种,有兴趣可以试试。回到DispatcherServlet#doDispatch()方法继续往下看,代码如下:

执行拦截器的前置方法,就是遍历HandlerExecutionChain的interceptorList属性,执行HandlerInterceptor#preHandle()方法,代码如下:

执行拦截器的后置方法,也是如此,代码如下:

当然,如果执行某个拦截器的前置方法,返回的是 false,则直接return,不往下走了。在执行拦截器的前置方法后,后置方法前,会执行HandlerAdapter#handle()方法,代码如下:



顺便说一下,RequestMappingHandlerAdapter也实现了InitializingBean接口,因此也会实现afterPropertiesSet()方法,代码如下:




这里设置的这些默认处理器,后续会用到,包括参数的解析、绑定、返回值的处理等。
再看看 RequestMappingHandlerAdapter#invokeAndHandle()方法,这里是执行目标类中目标方法的关键,代码如下:





以PathVariableMethodArgumentResolver为例看看,代码如下:

回到InvocableHandlerMethod#getMethodArgumentValues()方法,看看是如何解析参数的,主要看:HandlerMethodArgumentResolverComposite#resolveArgument()方法,代码如下:

还是以PathVariableMethodArgumentResolver,看看它的resolveArgument()方法,代码如下:




POST请求为:http://127.0.0.1:9000/hello/sayHello/张三



最后将解析获取到的参数值,放入Request的Attribute中。当然,除了这个之外,常用的是在请求体中会放入Json数据,最后参数中接受的是一个对象,SpringMVC是怎么对Json格式的数据进行处理的呢?这里是通过RequestResponseBodyMethodProcessor处理的,代码如下:



一共有八个转换器,这是在哪里添加的呢?后面讲@EnableWebMvc注解的时候会讲到,再看看HttpMessageConverter#read()方法,代码如下:








到这里可以知道,接收的Java对象,必须有Setter方法,因为这里是直接通过反射调用Setter方法进行赋值的。到这个为止,完成了Json的解析以及对象的赋值,即下图:

还有一些其他的注解,比如@RequestParam,感兴趣的自己研究。InvocableHandlerMethod#invokeForRequest(),参数处理完了,再调用InvocableHandlerMethod#doInvoke()方法,代码如下:


反射调用玩目标方法后,会有返回值,也需要处理返回值。回到ServletInvocableHandlerMethod#invokeAndHandle()方法,代码如下:



由于返回的是对象,因此需要处理,由于HelloController上添加的是@RestController,这个注解相当于@Controller+@ResponseBody,因此最终讲返回的对象反序列化为Json格式的数据,并返回给前端。这里还是需要看RequestResponseBodyMethodProcessor#handleReturnValue()方法,代码如下:

该方法较长,我只截取重点讲。
















到这里为止,把返回的对象进行序列化,只是将其转成字符并缓存起来,回到ObjectWriter#writeValue()方法,代码如下:



到这里为止,SpringMVC的返回值处理讲完了,SpringMVC剩下的内容将在下一篇博文中讲,敬请期待~
相关文章:
SpringMVC源码深度解析(中)
接上一遍博客《SpringMVC源码深度解析(上)》继续聊。最后聊到了SpringMVC的九大组建的初始化,以 HandlerMapping为例,SpringMVC提供了三个实现了,分别是:BeanNameUrlHandlerMapping、RequestMappingHandlerMapping、RouterFunctio…...
Mojo模型动态批处理:智能预测的终极武器
标题:Mojo模型动态批处理:智能预测的终极武器 在机器学习领域,模型的灵活性和可扩展性是至关重要的。Mojo模型(Model-as-a-Service)提供了一种将机器学习模型部署为服务的方式,允许开发者和数据科学家轻松…...
人、智能、机器人……
在遥远的未来之城,智能时代如同晨曦般照亮了每一个角落,万物互联,机器智能与人类智慧交织成一幅前所未有的图景。这座城市,既是科技的盛宴,也是人性与情感深刻反思的舞台。 寓言:《智光与心影》 在智能之…...
SpringCloud------Sentinel(微服务保护)
目录 雪崩问题 处理方式!!!技术选型 Sentinel 启动命令使用步骤引入依赖配置控制台地址 访问微服务触发监控 限流规则------故障预防流控模式流控效果 FeignClient整合Sentinel线程隔离-------故障处理线程池隔离和信号量隔离编辑 两种方式优缺点设置方式 熔断降级-----…...
【无标题】Elasticsearch for windows
一、windows安装Elasticsearch 1、Elasticsearch:用于存储数据、计算和搜索; 2、Logstash/Beats:用于数据搜集 3、Kibana:用于数据可视化 以上三个被称为ELK,常用语日志搜集、系统监控和状态分析 Elasticsearch安…...
Yolo-World网络模型结构及原理分析(一)——YOLO检测器
文章目录 概要一、整体架构分析二、详细结构分析YOLO检测器1. Backbone2. Head3.各模块的过程和作用Conv卷积模块C2F模块BottleNeck模块SPPF模块Upsampling模块Concat模块 概要 尽管YOLO(You Only Look Once)系列的对象检测器在效率和实用性方面表现出色…...
WEB前端06-BOM对象
BOM浏览器对象模型 浏览器对象模型:将浏览器的各个组成部分封装成对象。是用于描述浏览器中对象与对象之间层次关系的模型,提供了独立于页面内容、并能够与浏览器窗口进行交互的对象结构。 组成部分 Window:浏览器窗口对象 Navigator&…...
Android11 framework 禁止三方应用开机自启动
Android11应用自启动限制 大纲 Android11应用自启动限制分析验证猜想:Android11 AOSP是否自带禁止三方应用监听BOOT_COMPLETED方案禁止执行非系统应用监听到BOOT_COMPLETED后的代码逻辑在执行启动时判断其启动的广播接收器一棍子打死方案(慎用&#…...
Java | Leetcode Java题解之第263题丑数
题目: 题解: class Solution {public boolean isUgly(int n) {if (n < 0) {return false;}int[] factors {2, 3, 5};for (int factor : factors) {while (n % factor 0) {n / factor;}}return n 1;} }...
将AWS RDS MySQL实例从存储未加密改为加密的方案
问题描述: 因为AWS RDS官方文档【1】中已经明确说明,MySQL RDS的存储为EBS卷,用KMS进行RDS加密有如下限制: 您只能在创建RDS的时候,选择加密。对于已经创建的RDS实例,您无法将为加密的实例,直…...
nginx的配置:TLSv1 TLSv1.1 被暴露不安全
要在 Nginx 配置中禁用不安全的 SSL 协议(如 TLSv1 和 TLSv1.1),并仅启用更安全的协议(如 TLSv1.2 和 TLSv1.3),您可以更新您的 Nginx 配置文件。下面是一个示例配置: # 位于 Nginx 配置文件 (…...
揭开黑箱:目标检测中可解释性的重要性与实现
揭开黑箱:目标检测中可解释性的重要性与实现 在深度学习的目标检测任务中,模型的准确性虽然重要,但模型的决策过程是否透明也同样关键。可解释性(Explainability)是指模型能够为其预测结果提供清晰、可理解的解释。本…...
Mysql高价语句
一.高级语法的查询语句 1.排序语法(默认的排序方式就是升序)。 升序ASC:select * from test01 order by name; 降序DESC:select * from test01 order by name desc; 多个列排序:以多个列作为排序,只有第一…...
ArcGIS Pro SDK (九)几何 6 包络
ArcGIS Pro SDK (九)几何 6 包络 文章目录 ArcGIS Pro SDK (九)几何 6 包络1 构造包络2 构造包络 - 从 JSON 字符串3 合并两个包络4 与两个包络相交5 展开包络6 更新包络的坐标 环境:Visual Studio 2022 .NET6 ArcGI…...
单链表<数据结构 C版>
目录 概念 链表的单个结点 链表的打印操作 新结点的申请 尾部插入 头部插入 尾部删除 头部删除 查找 在指定位置之前插入数据 在任意位置之后插入数据 测试运行一下: 删除pos结点 删除pos之后结点 销毁链表 概念 单链表是一种在物理存储结构上非连续、非顺序…...
监控电脑进程,避免程序在打开前就已经在运行
文章目录 一、文章的目的(适用于windows)二、处理方式三、进程查看的内容在窗口端的演示四、附上代码例子四、通过os.kill的方式,再回到原来的表格时,会出现如下错误提示: 一、文章的目的(适用于windows&am…...
【MySQL进阶篇】存储对象:视图、存储过程及触发器
一、视图 1、介绍 视图(view)是一种虚拟存在的表。视图中的数据并不在数据库中实际存在,行和列数据来定义视图的查询中使用的表(基表),并且是在使用视图时动态生成的。 通俗的讲,视图只保存了…...
算法day05 master公式估算递归时间复杂度 归并排序 小和问题 堆排序
2.认识O(NlogN)的排序_哔哩哔哩_bilibili master公式 有这样一个数组:【0,4,2,3,3,1,2】;假设实现了这样一个sort()排序方法, 将数组二分成左右两等分,使用so…...
基于jeecgboot-vue3的Flowable流程仿钉钉流程设计器-支持VForm3表单的选择与支持
因为这个项目license问题无法开源,更多技术支持与服务请加入我的知识星球。 1、初始化的时候加载表单 /** 查询表单列表 */ const getFormList () > {listForm().then(res > formOptions.value res.result.records) } 2、开始节点的修改,增加表…...
【刷题汇总 -- 压缩字符串(一)、chika和蜜柑、 01背包】
C日常刷题积累 今日刷题汇总 - day0181、压缩字符串(一)1.1、题目1.2、思路1.3、程序实现 2、chika和蜜柑2.1、题目2.2、思路2.3、程序实现 3、 01背包3.1、题目3.2、思路3.3、程序实现 -- dp 4、题目链接 今日刷题汇总 - day018 1、压缩字符串(一) 1.1、题目 1.2、思路 读完…...
Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)
引言:为什么 Eureka 依然是存量系统的核心? 尽管 Nacos 等新注册中心崛起,但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制,是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...
华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建
华为云FlexusDeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色,华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型,能助力我们轻松驾驭 DeepSeek-V3/R1,本文中将分享如何…...
全志A40i android7.1 调试信息打印串口由uart0改为uart3
一,概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本:2014.07; Kernel版本:Linux-3.10; 二,Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01),并让boo…...
selenium学习实战【Python爬虫】
selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...
MFC 抛体运动模拟:常见问题解决与界面美化
在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...
Chrome 浏览器前端与客户端双向通信实战
Chrome 前端(即页面 JS / Web UI)与客户端(C 后端)的交互机制,是 Chromium 架构中非常核心的一环。下面我将按常见场景,从通道、流程、技术栈几个角度做一套完整的分析,特别适合你这种在分析和改…...
【LeetCode】算法详解#6 ---除自身以外数组的乘积
1.题目介绍 给定一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O…...
comfyui 工作流中 图生视频 如何增加视频的长度到5秒
comfyUI 工作流怎么可以生成更长的视频。除了硬件显存要求之外还有别的方法吗? 在ComfyUI中实现图生视频并延长到5秒,需要结合多个扩展和技巧。以下是完整解决方案: 核心工作流配置(24fps下5秒120帧) #mermaid-svg-yP…...
CppCon 2015 学习:Time Programming Fundamentals
Civil Time 公历时间 特点: 共 6 个字段: Year(年)Month(月)Day(日)Hour(小时)Minute(分钟)Second(秒) 表示…...
Axure零基础跟我学:展开与收回
亲爱的小伙伴,如有帮助请订阅专栏!跟着老师每课一练,系统学习Axure交互设计课程! Axure产品经理精品视频课https://edu.csdn.net/course/detail/40420 课程主题:Axure菜单展开与收回 课程视频:...
