【Spring6源码・MVC】请求处理流程源码解析
上一篇《【Spring6源码・MVC】初始化registry,完成url和controller的映射关系》我们知道,在IOC容器加载的同时,初始化了registry这个HashMap,这个HashMap中存放了请求路径和对应的方法。当我们请求进来,会通过这个registry去获取对应的方法。
在SpringBoot项目启动的时候,会加载一个线程:

步入run方法:

步入run方法:
当前这个类是Worker:Class Worker 主要维护运行任务的线程的中断控制状态,以及其他次要簿记。此类适时地扩展了 AbstractQueuedSyncer,以简化获取和释放围绕每个任务执行的锁。这可以防止旨在唤醒等待任务的工作线程而不是中断正在运行的任务的中断。我们实现了一个简单的非重入互斥锁,而不是使用 ReentrantLock,因为我们不希望工作线程任务在调用池控制方法(如 setCorePoolSize)时能够重新获取锁。此外,为了在线程实际开始运行任务之前抑制中断,我们将锁定状态初始化为负值,并在启动时清除它(在 runWorker 中)。
这个run方法主要是启动主运行循环,以此从队列中取出task执行。

当我们发送一个请求时:http://localhost:8081/user/test
会调用这个runWorker方法中的task.run():
runWorker这个方法是主工作线程运行循环。反复从队列中获取任务并执行它们,同时处理许多问题:
- 我们可以从初始任务开始,在这种情况下,我们不需要获取第一个任务。否则,只要池正在运行,我们就从getTask获取任务。如果它返回 null,则工作线程由于池状态或配置参数更改而退出。其他退出是由外部代码中的异常抛出引起的,在这种情况下,complete突然持有,这通常会导致processWorkerExit替换此线程。
- 在运行任何任务之前,获取锁以防止在任务执行时出现其他池中断,然后我们确保除非池停止,否则该线程没有设置中断。
- 每次任务运行之前都会调用 beforeExecute,这可能会引发异常,在这种情况下,我们会导致线程死亡(以 completeA 突然为 true 中断循环)而不处理任务。
- 假设 beforeExecute 正常完成,我们运行任务,收集其抛出的任何异常以发送到 afterExecute。我们分别处理 RuntimeException、Error(规范保证我们捕获这两个错误)和任意 Throwable。因为我们无法在 Runnable.run 中重新抛出 Throwable,所以我们在出路时将它们包装在 Errors 中(到线程的 UncaughtExceptionHandler)。任何抛出的异常也保守地导致线程死亡。
- task.run 完成后,我们调用 afterExecute,这也可能会抛出异常,这也会导致线程死亡。根据 JLS Sec 14.20,即使 task.run 抛出,此异常也会生效。异常机制的净效果是,afterExecute 和线程的 UncaughtExceptionHandler 具有尽可能准确的信息,我们可以提供有关用户代码遇到的任何问题的信息。

当我们发送http://localhost:8081/user/test请求时,步入这个task.run()方法中:

步入doRun方法中:

…
…
…
最终经过数十个调用,会在一个filterChanin链中调用servlet.service(request, response)方法:

然后会判断当前请求中的方法是否包含规定的方法:HTTP_SERVLET_METHODS.contains(request.getMethod())

然后会调用doGet方法:

之后调用经典的doService方法:

步入doService方法之后,又会调用doDispatch方法:

看一看这个核心方法:doDispatch。

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {...try {...try {// 检查是否文件请求processedRequest = checkMultipart(request);...// 根据请求找到对应的控制器执行器链HandlerExecutionChain mappedHandler = getHandler(processedRequest);...// 找到对应控制器的适配器,用来执行操作的HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());...// 执行拦截器前置if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// 真正执行控制器逻辑mv = ha.handle(processedRequest, response, mappedHandler.getHandler());...// 执行拦截器后置mappedHandler.applyPostHandle(processedRequest, response, mv);}...// 处理返回结果processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}...
}
先看checkMultipart(request)方法,它是用来检查是否是文件上传,如果有文件上传,则将request包装成StandardMultipartHttpServletRequest
关于mappedHandler = getHandler(processedRequest);和HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());这两个方法,和前面AOP讲到的获取拦截器和适配器的逻辑差不多,循环去匹配,这里就不展开了。
重点看一下mv = ha.handle(processedRequest, response, mappedHandler.getHandler());这个真正执行控制器逻辑的方法。
步入handle方法:最终来到这:

步入invokeHandlerMethod方法:
首先创建ServletInvocableHandlerMethod对象,再去调用。

步入invokeAndHandle方法:
首先去执行控制器的逻辑,如果有结果再去处理结果。

看看如何执行控制器的逻辑的,步入invokeForRequest方法:
首先获取方法参数,再去invoke:
如何实现的就不看了吧,最后肯定是调用本地方法去执行控制器的逻辑。
感兴趣可以翻一翻我的博客,有一篇是如何解析本地方法的。可以看看cpp是如何实现的。

最后,我们能得到返回的结果:

最后会去调用这行代码:
this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
步入handleReturnValue方法:

步入handleReturnValue方法:

步入writeWithMessageConverters方法:
最后经过层层包装,调用((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);方法:

步入write方法:

步入writeInternal方法:

步入copy方法:
落叶归根,结束。

其实关于SpringMVC还有很多没写,但是首先知道这个流程就基本够用了,之后用到哪些的哪些的时候,再按着这个流程看就好。
值得一提的是我们这个demo没有文件,也没有参数,所以撸流程还是很容易的,如果有参数还要注意是如何解析参数的,如果用@RequestParam注解的话,直接通过反射就可以获取到,Spring也提供了处理这个注解的解析器,如果不加注解,会默认使用名称绑定,底层用asm框架读取字节码来获取参数名称,所以编码记得用@RequestParam声明参数,之后会放进一个缓存数组中,在ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);代码执行时,就会封装成ServletInvocableHandlerMethod 对象。
其他的暂且先不提了,都比较简单,点点看看就行了。
相关文章:
【Spring6源码・MVC】请求处理流程源码解析
上一篇《【Spring6源码・MVC】初始化registry,完成url和controller的映射关系》我们知道,在IOC容器加载的同时,初始化了registry这个HashMap,这个HashMap中存放了请求路径和对应的方法。当我们请求进来,会通过这个regi…...
elasticsearch term match 查询
1. 准备数据 PUT h1/doc/1 {"name": "rose","gender": "female","age": 18,"tags": ["白", "漂亮", "高"] }PUT h1/doc/2 {"name": "lila","gender&quo…...
canal使用说明:MySQL、Redis实时数据同步
1. canal简介 canal是阿里开源的数据同步工具,基于bin log可以将数据库同步到其他各类数据库中,目标数据库支持mysql,postgresql,oracle,redis,MQ,ES等 canal分成服务端deployer和客户端adapter,我们可以部署多个,同时为了方便管…...
计算机视觉框架OpenMMLab开源学习(三):图像分类实战
前言:本篇主要偏向图像分类实战部分,使用MMclassification工具进行代码应用,最后对水果分类进行实战演示,本次环境和代码配置部分省略,具体内容建议参考前一篇文章:计算机视觉框架OpenMMLab开源学习&#x…...
awk命令
一.介绍 awk是专门为文本处理设计的编程语言,是一门数据驱动的编程语言。与sed类似,都是以数据驱动的行处理软件,主要用于数据扫描,过滤和汇总。数据可以来自于标准输入,管道或者文件。 二.语法 awk是一种处理文本文件…...
LocalDateTime获取时间的年、月、日、时、分、秒、纳秒
如何把String/Date转成LocalDateTime参考String、Date与LocalDate、LocalTime、LocalDateTime之间互转 String、Date、LocalDateTime、Calendar与时间戳之间互相转化参考String、Date、LocalDateTime、Calendar与时间戳之间互相转化 方法介绍 getYear() 获取日期的年 getMon…...
MoveIT Rviz和Gazebo联合仿真
文章目录环境安装概述ros_control框架ros_control数据流文件配置附加工具故障问题解决参考接前两篇:ROS MoveIT1(Noetic)安装总结 Solidworks导出为URDF用于MoveIT总结(带prismatic) MoveIT1 Assistant 总结 环境 Ubu…...
ESP32S2(12K)-DS18B20数码管显示温度
一、物料清单: NODEMCU-32-S2 (ESP32-12K)四段数码管(共阴)DS18B20(VCC/DQ/GND)Arduino-IDE 2.0.3二、实现方法及效果图: 2.1 引用库 // #include <OneWire.h> //可以不引入,因为DallasTemperature.h中已经引入了OneWire.h #include <DallasTemperature.h>#…...
linux栈溢出定位
一、编译选项定位堆栈溢出 来源:堆栈溢出检测机制 - SkrSky - 博客园 1、栈溢出可能打印 unhandled level 1 translation fault (11) at 0x7f8d0347, esr 0x92000005 2、栈溢出保护机制 gcc提供了栈保护机制stack-protector(编译选项-fstack-protec…...
CSS基础:选择器和声明样式
CSS概念 CSS(Cascading Style Sheets)层叠样式表,又叫级联样式表,简称样式表 CSS用于HTML文档中元素样式的定义 使用css让网页具有美观一致的页面 语法 CSS 规则由两个主要的部分构成:选择器和声明样式 选择器通常…...
VS中安装gismo库
文章目录前言一、下载安装paraview直接下载压缩包安装就可以了解压后按步骤安装即可二、gismo库的安装gismo库网址第一种方法:第二种方法第三种方法:用Cmake软件直接安装首先下载cmake软件[网址](https://cmake.org/download/)安装gismo库三、gismo库的使…...
元学习方法解决CDFSL以及两篇SOTA论文讲解
来源:投稿 作者:橡皮 编辑:学姐 带你学习跨域小样本系列1-简介篇 跨域小样本系列2-常用数据集与任务设定详解 跨域小样本系列3:元学习方法解决CDFSL以及两篇SOTA论文讲解(本篇) 跨域小样本系列4…...
大数据之------------数据中台
一、什么是数据中台 **数据中台是指通过数据技术,对海量数据进行采集、计算、存储、加工,同时统一标准和口径。**数据中台的目标是让数据持续用起来,通过数据中台提供的工具、方法和运行机制,把数据变为一种服务能力,…...
Python 中 字符串是什么?
字符串是 Python 中最常用的数据类型。我们可以使用引号 ( ’ 或 " ) 来创建字符串。 创建字符串很简单,只要为变量分配一个值即可。例如: var1 ‘Hello World!’ var2 “Python Runoob” Python 访问字符串中的值 Python 不支持单字符类型&…...
OJ刷题Day1 · 一维数组的动态和 · 将数字变成 0 的操作次数 · 最富有的客户资产总量 · Fizz Buzz · 链表的中间结点 · 赎金信
一、一维数组的动态和二、将数字变成 0 的操作次数三、最富有的客户资产总量四、Fizz Buzz五、链表的中间结点六、赎金信一、一维数组的动态和 给你一个数组 nums 。数组「动态和」的计算公式为:runningSum[i] sum(nums[0]…nums[i]) 。 请返回 nums 的动态和。 示…...
【数据结构】栈——必做题
逆波兰表达式后缀表达式的出现是为了方便计算机处理,它的运算符是按照一定的顺序出现,所以求值过程中并不需要使用括号来指定运算顺序,也不需要考虑运算符号(比如加减乘除)的优先级。先介绍中简单的人工转化方法&#…...
LearnOpenGL 笔记 - 入门 04 你好,三角形
系列文章目录 LearnOpenGL 笔记 - 入门 01 OpenGLLearnOpenGL 笔记 - 入门 02 创建窗口LearnOpenGL 笔记 - 入门 03 你好,窗口 文章目录系列文章目录前言你好,三角形顶点输入顶点着色器(Vertex Shader)编译着色器片段着色器&…...
keepalived+mysql高可用
一.设置mysql同步信息两节点安装msyql略#配置节点11.配置权限允许远程访问mysql -u root -p grant all on *.* to root% identified by Root1212# with grant option; flush privileges;2.修改my.cnf#作为主节点配置(节点1)#作为主节点配置 server-id 1 …...
JAVA工具篇--1 Idea中 Gradle的使用
前言: 既然我们已经使用Maven 来完成对项目的构建,为什么还要使用Gradle 进行项目的构建;gradle和maven都可以作为java程序的构建工具,但两者还是有很大的不同之处的:1.可扩展性,gradle比较灵活,…...
弄懂自定义 Hooks 不难,改变开发认知有点不习惯
前言 我之前总结逻辑重用的时候,就一直在思考一个问题。 对于逻辑复用,render props 和 高阶组件都可以实现,同样官方说 Hooks 也可以实现,且还是在不增加额外的组件的情况下。 但是我在项目代码中,没有找到自定义 …...
Karpathy投奔Anthropic:一个顶级AI天才的四次人生豪赌
5月19日,一条推文炸了整个AI圈。 Andrej Karpathy——OpenAI联合创始人、前特斯拉AI总监、AI教育布道师——宣布加入Anthropic。 英伟达具身智能负责人Jim Fan评论说:"这比Google I/O的Keynote更重磅。" 网友打了个比方:"堪…...
DeepStream9.0 service-maker
service-maker在前几个版本就推出了,DeepStream9.0做了增强: Added Pyservice maker support for Smart-Recording(就是实时录制码流) 如果你用过 NVIDIA DeepStream,应该很熟悉它的典型开发方式:围绕 G…...
惠普OMEN游戏本性能解放终极指南:OmenSuperHub完全使用教程
惠普OMEN游戏本性能解放终极指南:OmenSuperHub完全使用教程 【免费下载链接】OmenSuperHub 使用 WMI BIOS控制性能和风扇速度,自动解除DB功耗限制。 项目地址: https://gitcode.com/gh_mirrors/om/OmenSuperHub 还在为官方Omen Gaming Hub的臃肿和…...
Barlow字体完整指南:如何用54种样式提升你的设计专业度
Barlow字体完整指南:如何用54种样式提升你的设计专业度 【免费下载链接】barlow Barlow: a straight-sided sans-serif superfamily 项目地址: https://gitcode.com/gh_mirrors/ba/barlow Barlow是一款专为现代设计而生的开源字体家族,以其独特的…...
2026论文降AIGC网站:11款工具实测谁才是真神器?
2026 年学术审核标准持续收紧,论文重复率、AIGC 检出率已经成为毕业答辩、期刊投稿的硬性门槛。随着知网、维普、Turnitin 等主流检测平台算法不断优化升级,对论文原创性和人工写作痕迹的要求愈发严格。面对日益严苛的审查机制,越来越多学生和…...
Taotoken用量看板与成本管理功能实操体验
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 Taotoken用量看板与成本管理功能实操体验 在将多个大模型API集成到实际项目中时,除了对接的便利性,团队往往…...
2026学数据分析对产品岗位的价值分析
一、数据分析在产品岗位中的核心价值数据分析能力帮助产品经理更精准地理解用户需求,优化产品决策。通过数据驱动的方法,减少主观臆断,提升产品迭代效率。数据可视化工具(如Tableau、Power BI)和统计分析能力ÿ…...
ubuntu 播放器 播放此文件需要H.264(high profile)解码器,但是没有安装
解决方法: sudo apt install gstreamer1.0-plugins-bad gstreamer1.0-libav...
本地AI工具炸场!一周GitHub星标破万,云端AI正在向你的电脑迁移
2026年5月中旬,三个开源项目突然火了: Hermes Agent,连续3天登顶OpenRouter调用量榜首,累计消耗6.72万亿tokens ds4.c,Redis之父Salvatore Sanfilippo专为DeepSeek V4 Flash打造的推理引擎,发布不到一周获2600+星 DeepSeek-TUI,终端AI编程工具,上线四个月获3700+星,…...
KMS_VL_ALL_AIO:告别激活烦恼的完整解决方案指南
KMS_VL_ALL_AIO:告别激活烦恼的完整解决方案指南 【免费下载链接】KMS_VL_ALL_AIO Smart Activation Script 项目地址: https://gitcode.com/gh_mirrors/km/KMS_VL_ALL_AIO 你是否曾经为了激活Windows系统而花费数小时研究复杂的命令行?或者面对O…...
