SpringMVC的执行流程剖析和源码跟踪

目录

    • 一、常用组件:
      • 1、DispatcherServlet
      • 2、HandlerMapping
      • 3、Handler
      • 4、HandlerAdapter:
      • 5、ViewResolver
      • 6、View
    • 二、SpringMVC的执行流程:
      • 1、流程图
    • 在这里插入图片描述
      • 2、文字解析流程图
      • 3、ContextLoaderListener
    • 三、源码跟踪
      • 1、doService()方法
      • 2、doDispatch()方法逻辑分解
        • 2.1 初始化变量
        • 2.2 检查是否为文件上传等多部分请求
        • 2.3 获取处理器
        • 2.4 获取处理器适配器
        • 2.5 处理 Last-Modified 头部
        • 2.6 执行处理器前的拦截器
        • 2.7 执行处理器逻辑
        • 2.8 处理异步请求
        • 2.9 应用默认视图名称
        • 2.10 执行处理器后的拦截器
        • 2.11 处理异常
        • 2.12 处理分发结果
        • 2.13 清理资源
        • 2.14 请求处理阶段如何异常处理?

一、常用组件:

1、DispatcherServlet

  • 前端控制器,不需要工程师开发,由框架提供.
  • 作用:统一处理请求和响应,整个流程的控制中心,由他调用其它组件处理用户的请求

2、HandlerMapping

  • 处理映射器,不需要工程师开发,由框架提供
  • 作用:根据请求的url,method等信息查找Handler,即控制器方法

3、Handler

  • 处理器,需要工程师开发
  • 作用:在DispatcherServlet的控制器Handler对具体的用户请求进行处理

4、HandlerAdapter:

  • 处理适配器,不需要供货才能是开发,由框架提供
  • 作用:通过HandlerAdapter催处理器进行执行

5、ViewResolver

  • 视图解析器,不需要工程师开发,由框架提供
  • 作用:进行视图解析,得到响应的视图,如:ThymeleafView,InternalResourceView,RedirectView

6、View

  • 视图:简单理解就是可视化界面
  • 作用:将模型数据通过页面展示给用户

二、SpringMVC的执行流程:

1、流程图

在这里插入图片描述

2、文字解析流程图

以下流程的代码DispatcherServletdoService方法中调用的doDispatch方法中找到。

  • 用户向服务器发送请求,请求被SpringMVC前端控制器DispatcherServlet捕获。
    • 为啥请求能被DispatcherServlet捕获?
      • 因为DispatcherServlet间接继承了HttpServlet
      • 在这里插入图片描述
  • DispatcherServlet 对请求URL进行解析,得到请求资源标识符(URL),判断请求URL对应的映射:
  • 不存在:
    • 再判断是否配置了mvc:default-servlet-handler
    • 如果没设置,则控制台包映射查找不到,客户端展示404错误
  • 存在:
    • 根据该URL,调用HandlerMapping获得该Handler配置的所有先关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain执行链对象的形式返回
    • DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter
    • 如果成果获得HandlerAdapter,测试将开始执行拦截器的preHander(…)方法[正向]
    • 提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)方法,处理请求,在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:
      • HttpMessageConveter:将请求消息,转换成一个对象,将对象转换为指定的响应信息
      • 数据转换:对请求信息进行数据转换,如String转换成Integer,Double等
      • 数据格式化:对请求消息进行格式化,如将字符串准换成格式化数字或格式化日期等
      • 数据验证:验证数据的有效性(长度,格式等),验证结果存储到BindingResult或Error中
  • Handler执行完毕后,向DispatcherServlet 返回一个ModelAndView对象
  • 此时将开始执行拦截器的postHandle(…)方法[逆向]
  • 根据返回的ModelAndView(此时会判断是否存在异常:如果存在异常,则执行HandlerExceptionResover进行异常处理)选择一个适合的ViewResolver进行视图解析,根据Model和View,来渲染视图。
  • 渲染视图完毕执行拦截器的afterCompletion(…)方法[逆向]
  • 将渲染视图结果返回给客户端.

3、ContextLoaderListener

  • Spring提供了监听器ContextLoaderListener,实现ServeltContextListener接口,可监听ServletContext的状态,在web服务器的启动,读取Spring的配置文件,创建Spring的IOC容器,web应用中不许在web.xml中配置.
<listener>
//配置Spring的监听器,在服务启动时加载Spring的配置文件,Spring配置文件默认位置和名称:/WEB-INF/applicationContext.xml,通过上下文参数自动以Spring配置文件的位置和名称
<listener-class>org.springframework.web.context.ContextLoaderListener<listenern-class>
</listener>

三、源码跟踪

1、doService()方法

一切请求始于DispatcherServlet的doService()方法。

	@Overrideprotected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {//logRequest 方法通常用于记录请求相关信息。logRequest(request);// 使SpringMVC能处理程序和视图对象可用。request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());if (this.flashMapManager != null) {FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);if (inputFlashMap != null) {request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));}request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);}try {//doDispatch 方法是 Spring MVC 请求处理的核心逻辑,涵盖了从请求解析到响应生成的完整流程。doDispatch(request, response);}finally {if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {// 如果包含,就恢复原始属性快照。if (attributesSnapshot != null) {restoreAttributesAfterInclude(request, attributesSnapshot);}}}}

2、doDispatch()方法逻辑分解

doDispatch 是 Spring MVC 框架中的核心方法,用于处理 HTTP 请求并将其分发到相应的处理器(Handler)。它是 DispatcherServlet 的关键部分,负责协调请求的整个生命周期,包括请求解析、处理器选择、视图渲染和异常处理。

2.1 初始化变量

定义了多个变量,如 processedRequest、mappedHandler、multipartRequestParsed 等,用于存储请求处理过程中的中间状态。
获取 WebAsyncManager 实例,用于管理异步请求

HttpServletRequest processedRequest = request;HandlerExecutionChain mappedHandler = null;boolean multipartRequestParsed = false;WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);try {ModelAndView mv = null;Exception dispatchException = null;

2.2 检查是否为文件上传等多部分请求

调用 checkMultipart 方法检查请求是否包含文件上传等多部分数据。如果是,则返回一个新的封装请求对象。

   processedRequest = checkMultipart(request);multipartRequestParsed = (processedRequest != request);

2.3 获取处理器

调用 getHandler 方法根据请求找到对应的处理器(Handler)。如果没有找到处理器,则调用 noHandlerFound 方法返回 404 错误。

  mappedHandler = getHandler(processedRequest);if (mappedHandler == null) {noHandlerFound(processedRequest, response);return;}

里面通过处理映射器handlerMappings来获取Handler.最终返回一个执行链HandlerExecutionChain

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {if (this.handlerMappings != null) {for (HandlerMapping mapping : this.handlerMappings) {HandlerExecutionChain handler = mapping.getHandler(request);if (handler != null) {return handler;}}}return null;}

2.4 获取处理器适配器

根据处理器类型获取对应的处理器适配器(HandlerAdapter),用于执行处理器逻辑。

HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

2.5 处理 Last-Modified 头部

检查请求是否可以被缓存(通过 Last-Modified 和 If-Modified-Since 头部判断)。
如果缓存命中,则直接返回 304 响应。

 long lastModified = ha.getLastModified(request, mappedHandler.getHandler());if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {return;}

2.6 执行处理器前的拦截器

调用拦截器链的 preHandle 方法,允许拦截器在处理器执行前进行预处理。
如果拦截器返回 false,则终止请求处理。

 if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}

2.7 执行处理器逻辑

调用处理器适配器的 handle 方法执行实际的业务逻辑,并返回一个 ModelAndView 对象。

   mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

2.8 处理异步请求

如果请求是异步的,则直接返回,后续由异步机制继续处理。

 if (asyncManager.isConcurrentHandlingStarted()) {return;}

2.9 应用默认视图名称

如果 ModelAndView 中没有指定视图名称,则应用默认视图名称。

   applyDefaultViewName(processedRequest, mv);

2.10 执行处理器后的拦截器

调用拦截器链的 postHandle 方法,在处理器执行后进行后处理。

    mappedHandler.applyPostHandle(processedRequest, response, mv);

2.11 处理异常

如果在请求处理过程中抛出异常,则捕获并记录异常信息。
如果是普通异常,则直接捕获;如果是严重错误(如 Throwable),则包装为 NestedServletException。

catch (Exception ex) {dispatchException = ex;}catch (Throwable err) {// As of 4.3, we're processing Errors thrown from handler methods as well,// making them available for @ExceptionHandler methods and other scenarios.dispatchException = new NestedServletException("Handler dispatch failed", err);}

2.12 处理分发结果

调用 processDispatchResult 方法处理分发结果,包括渲染视图或返回响应。

    processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);  

2.13 清理资源

如果请求是多部分请求,则调用 cleanupMultipart 方法清理相关资源。
如果是异步请求,则调用 applyAfterConcurrentHandlingStarted 方法处理异步逻辑。

finally {if (asyncManager.isConcurrentHandlingStarted()) {// 代替postHandle和afterCompletionif (mappedHandler != null) {mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}}else {// 清理多部分请求使用的任何资源.if (multipartRequestParsed) {cleanupMultipart(processedRequest);}}}

2.14 请求处理阶段如何异常处理?
  • 在请求处理的每个阶段都可能抛出异常,doDispatch 方法通过多层 try-catch 结构捕获并处理这些异常。
  • 异常处理逻辑包括:
    • 触发 afterCompletion 方法,通知拦截器请求已完成。
    • 清理多部分请求资源。
    • 返回适当的错误响应。

结束
文章结束,喜欢就给个一键三连吧,你的肯定是我最大的动力,点赞上一千我就是脑瘫也出下章。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/38246.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LeetCode hot 100 每日一题(13)——73. 矩阵置零

这是一道难度为中等的题目&#xff0c;让我们来看看题目描述&#xff1a; 给定一个 _m_ x _n_ 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 提示&#xff1a; m matrix.lengthn matrix[0].length1 < m, n …

ubuntu 解挂载时提示 “umount: /home/xx/Applications/yy: target is busy.”

问题如题所示&#xff0c;我挂载一个squanfs文件系统到指定目录&#xff0c;当我使用完后&#xff0c;准备解挂载时&#xff0c;提示umount: /home/xx/Applications/yy: target is busy.&#xff0c;具体的如图所示&#xff0c; 这种提示通常是表明这个路径的内容正在被某些进…

跟着StatQuest学知识06-CNN进行图像分类

目录 一、CNN特点 二、CNN应用于图像分类 &#xff08;一&#xff09;使用过滤器 &#xff08;二&#xff09;通过ReLU激活函数 &#xff08;三&#xff09;应用新的滤波器&#xff08;池化&#xff09; &#xff08;四&#xff09;输入 &#xff08;五&#xff09;输出…

MATLAB 控制系统设计与仿真 - 27

状态空间的标准型 传递函数和状态空间可以相互转换&#xff0c;接下来会举例如何有传递函数转成状态空间标准型。 对角标准型 当 G(s)可以写成&#xff1a; 即&#xff1a; 根据上图可知&#xff1a; 约当标准型 当 G(s)可以写成&#xff1a; 即&#xff1a; 根据上图…

Python网络编程入门

一.Socket 简称套接字&#xff0c;是进程之间通信的一个工具&#xff0c;好比现实生活中的插座&#xff0c;所有的家用电器要想工作都是基于插座进行&#xff0c;进程之间要想进行网络通信需要Socket&#xff0c;Socket好比数据的搬运工~ 2个进程之间通过Socket进行相互通讯&a…

C++ --- 多态

1 多态的概念 多态(polymorphism)的概念&#xff1a;通俗来说&#xff0c;就是多种形态。多态分为编译时多态(静态多态)和运⾏时多 态(动态多态)&#xff0c;这⾥我们重点讲运⾏时多态&#xff0c;编译时多态(静态多态)和运⾏时多态(动态多态)。编译时 多态(静态多态)主要就是我…

MQTT的安装和使用

MQTT的安装和使用 在物联网开发中&#xff0c;mqtt几乎已经成为了广大程序猿必须掌握的技术&#xff0c;这里小编和大家一起学习并记录一下~~ 一、安装 方式1、docker安装 官网地址 https://www.emqx.com/zh/downloads-and-install/broker获取 Docker 镜像 docker pull e…

ROS多机通信功能包——Multibotnet

引言 这是之前看到一位大佬做的集群通信中间件&#xff0c;突发奇想&#xff0c;自己也来做一个&#xff0c;实现更多的功能、更清楚的架构和性能更加高效的ROS多机通信的功能包 链接&#xff1a;https://blog.csdn.net/benchuspx/article/details/128576723 Multibotnet Mu…

pfsense部署四(静态路由的配置)

目录 一 . 介绍 二 . 配置过程 一 . 介绍 pfsense开源防火墙经常在进行组网时&#xff0c;通常会用于连接不同的网络&#xff0c;在这个时候进需要给pfsense配置路由&#xff0c;而这篇文章介绍的是静态路由的配置 二 . 配置过程 拓扑图&#xff1a; 本次实验使用ensp模拟器…

干货!三步搞定 DeepSeek 接入 Siri

Siri高频用户福音&#xff0c;接下来仅需3步教你如何将 DeepSeek 接入 Siri&#xff01;虽然苹果公司并没有给国行产品提供 ai 功能&#xff0c;但是我们可以让自己的 iPhone 更智能一点。虽然有消息称苹果和阿里巴巴将合作为中国iPhone用户开发AI功能&#xff0c;但我们可以先…

自动学习和优化过程,实现更加精准的预测和决策的智慧交通开源了

智慧交通视觉监控平台是一款功能强大且简单易用的实时算法视频监控系统。它的愿景是最底层打通各大芯片厂商相互间的壁垒&#xff0c;省去繁琐重复的适配流程&#xff0c;实现芯片、算法、应用的全流程组合&#xff0c;从而大大减少企业级应用约95%的开发成本。通过高效的实时视…

DeepSeek R1 本地部署指南 (3) - 更换本地部署模型 Windows/macOS 通用

0.准备 完成 Windows 或 macOS 安装&#xff1a; DeepSeek R1 本地部署指南 (1) - Windows 本地部署-CSDN博客 DeepSeek R1 本地部署指南 (2) - macOS 本地部署-CSDN博客 以下内容 Windows 和 macOS 命令执行相同&#xff1a; Windows 管理员启动&#xff1a;命令提示符 CMD ma…

使用 Node.js 读取 Excel 文件并处理合并单元格

使用 Node.js 读取 Excel 文件并处理合并单元格 在现代的数据处理任务中&#xff0c;Excel 文件是一种非常常见的数据存储格式。无论是数据分析、报表生成&#xff0c;还是数据迁移&#xff0c;Excel 文件都扮演着重要的角色。然而&#xff0c;处理 Excel 文件时&#xff0c;尤…

汇川EASY系列之以太网通讯(MODBUS_TCP做从站)

汇川easy系列PLC做MODBUS_TCP从站,不需要任何操作,但是有一些需要知道的东西。具体如下: 1、汇川easy系列PLC做MODBUS_TCP从站,,ModbusTCP服务器默认开启,无需设置通信协议(即不需要配置),端口号为“502”。ModbusTCP从站最多支持31个ModbusTCP客户端(ModbusTCP主站…

1996-2023年各省公路里程数据(无缺失)

1996-2023年各省公路里程数据&#xff08;无缺失&#xff09; 1、时间&#xff1a;1996-2023年 2、来源&#xff1a;国家统计局、统计年鉴 3、指标&#xff1a;公路里程&#xff08;万公里&#xff09; 4、范围&#xff1a;31省 5、指标解释&#xff1a;公路里程指报告期末…

虚拟机访问主机的plc仿真

主机 虚拟机 默认&#xff0c;连接物理地址

从“不敢买大”到“按墙选屏”,海信电视如何凭百吋重构客厅?

电视买小了&#xff0c;成为茜茜新房入住后最大的遗憾。 新房装修的时候&#xff0c;茜茜担心电视买大了眼睛看着累&#xff0c;因此把尺寸选在了65吋。结果入住后&#xff0c;孩子看动画片嚷着“画面太小”&#xff0c;老公看球赛吐槽“看不清球员号码”&#xff0c;全家追剧…

Swift 经典链表面试题:如何在不访问头节点的情况下删除指定节点?

摘要 在日常开发中&#xff0c;链表虽然不像数组、字典那么常用&#xff0c;但在某些场景下还是挺重要的。尤其是面试的时候&#xff0c;链表题目可是经典考点之一。今天我们要聊的就是一个看似简单&#xff0c;但很多人第一次做都会卡住的问题——删除单链表中的指定节点。 …

楼宇自控系统的结构密码:总线与分布式结构方式的差异与应用

在现代建筑中&#xff0c;为了实现高效、智能的管理&#xff0c;楼宇自控系统变得越来越重要。它就像建筑的 智能管家&#xff0c;可自动控制照明、空调、通风等各种机电设备&#xff0c;让建筑运行更顺畅&#xff0c;还能节省能源成本。而在楼宇自控系统里&#xff0c;有两种关…

git | 回退版本 并保存当前修改到stash,在进行整合。[git checkout | git stash 等方法 ]

目录 一些常见命令&#xff1a; git 回退版本 一、临时回退&#xff08;不会修改历史&#xff0c;可随时回到当前版本&#xff09; 方法1&#xff1a;git checkout HEAD~1 问题&#xff1a;处于 detached HEAD 状态下提交的&#xff0c;无法直接 git push ✅ 选项 1&…