Spring MVC 的调用(12)
目录
SpringMVC流程
源码分析
第一步:用户发起请求到前端控制器(DispatcherServlet)
第二步:前端控制器请求处理器映射器(HandlerMappering)去查找处理器(Handle):通过xml配置或者注解进行查找
1. 根据请求的uri拿到对应的HandlerMethod对象
第三步:找到以后处理器映射器(HandlerMappering)像前端控制器返回执行链(HandlerExecutionChain)
第四步:前端控制器(DispatcherServlet)调用处理器适配器(HandlerAdapter)去执行处理器(Handler)
第五步:处理器适配器去执行Handler
第六步:Handler执行完给处理器适配器返回ModelAndView
第七步:处理器适配器向前端控制器返回ModelAndView
ModelAndView演示 :
第八步:前端控制器请求视图解析器(ViewResolver)去进行视图解析,并返回View
第九步:视图解析器像前端控制器返回View
第十步:前端控制器对视图进行渲染
第十一步:前端控制器向用户响应结果
SpringMVC流程

第一步:用户发起请求到前端控制器(DispatcherServlet)
第二步:前端控制器请求处理器映射器(HandlerMappering)去查找处理器(Handle):通过xml配置或者注解进行查找
第三步:找到以后处理器映射器(HandlerMappering)像前端控制器返回执行链(HandlerExecutionChain)
第四步:前端控制器(DispatcherServlet)调用处理器适配器(HandlerAdapter)去执行处理器(Handler)
第五步:处理器适配器去执行Handler
第六步:Handler执行完给处理器适配器返回ModelAndView
第七步:处理器适配器向前端控制器返回ModelAndView
第八步:前端控制器请求视图解析器(ViewResolver)去进行视图解析
第九步:视图解析器像前端控制器返回View
第十步:前端控制器对视图进行渲染
第十一步:前端控制器向用户响应结果
此处需要强调的是
DispatcherServlet:作为前端控制器,整个流程控制的中心,控制其它组件执行,统一调度,降低组件之间的耦合性,提高每个组件的扩展性。
HandlerMapping:通过扩展处理器映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。
HandlAdapter:通过扩展处理器适配器,支持更多类型的处理器。
ViewResolver:通过扩展视图解析器,支持更多类型的视图解析,例如:jsp、freemarker、pdf、excel等。
源码分析
第一步:用户发起请求到前端控制器(DispatcherServlet)
其实就是前端请求调用到了DispatcherServlet的doService方法,而doService方法又调用到了doDispatch 核心方法:

而 doDispatch 方法负责整个Spring MVC的调度与响应,是核心中的核心:

第二步:前端控制器请求处理器映射器(HandlerMappering)去查找处理器(Handle):通过xml配置或者注解进行查找
其实,就是在doDispatch内部调用了 getHandler 方法,它会获取到所有的handlerMapping对象进行遍历,找到符合匹配规则的handlerMapping就直接返回:
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {//handlerMappering实例if (this.handlerMappings != null) {for (HandlerMapping mapping : this.handlerMappings) {//获取HandlerMethod和过滤器链的包装类HandlerExecutionChain handler = mapping.getHandler(request);if (handler != null) {return handler;}}}return null;}
注意: 以上的for循环遍历所有的handlerMapping类,并且调用每个类的都调用了getHandler方法,是一个策略设计模式,可以避免大量的 if 判断。
这里,我们需要关注一下requestMappingHandlerMapping这个对象,因为上一篇博客中,我们提到了是在requestMappingHandlerMapping创建完以后建立映射关系的。遍历到requestMappingHandlerMapping对象后进入getHanlder方法 内部:

1. 根据请求的uri拿到对应的HandlerMethod对象
其实,就是调用 getHandlerInternal 方法的过程,上一讲我们提到根据 URL 和 requestMappingInfo建立了映射,而requestMappingInfo又和HandlerMethod建立了映射,此处肯定也是按照这个规则逐步去查找HandlerMethod对象的。 下面进行源码验证:

进入这个方法内部:
首先,我们发现它根据URL 拿到了 requestMappingInfo 对象:

其次,根据requestMappingInfo拿HandlerMethod对象,进入这个方法内部进行确认:

第三步:找到以后处理器映射器(HandlerMappering)像前端控制器返回执行链(HandlerExecutionChain)
主方法:

进入这个方法内部,看看具体是如何 获取 HandlerExecutionChain 对象的
补充说明一下:拦截器是我们自己定义的,它有3个方法分别问前置过滤器、中置过滤器 和 后置过滤器:
package com.xiangxue.jack.interceptor;import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;@Component
public class UserInterceptor implements HandlerInterceptor {//前置过滤器@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("======UserInterceptor用户权限校验=========");return true;}//中置过滤器@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("========UserInterceptor修改modelAndView======");}//后置过滤器@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("========UserInterceptor资源释放======");}
}
而拦截器是在 有 @EnableWebMvc的类, 并且这个类继承了 WebMvcConfigurerAdapter类 中添加进去的。

第四步:前端控制器(DispatcherServlet)调用处理器适配器(HandlerAdapter)去执行处理器(Handler)
回到核心方法 doDispatch 中,在这个方法内部调用了 getHandlerAdapter 进行获取处理器适配器的:

进入这个方法内部, 看看具体是如何获取到处理器适配器的:

第五步:处理器适配器去执行Handler
第六步:Handler执行完给处理器适配器返回ModelAndView
第七步:处理器适配器向前端控制器返回ModelAndView
第五、六、七步可以合起来一起分析。第三步的时候说到了拦截器,其实在执行hand方法之前会进行 拦截器 中的前置过滤器的调用:

成功进入到了我们自己写的拦截器类中,并且调用到了前置过滤器方法:

言归正传,处理器去执行handler,其实就是调用到我们的Controller类的对应方法而已。就是进入到业务类中,进行实际的业务调用。最终返回的数据类型包装成ModelAndView返回给处理器适配器, 而处理器适配器向前端控制器返回ModelAndView。

还需要补充一点,中置过滤器的调用时序, 是当 ha.handle 掉完以后, 也就是 Controller 里面具体方法调用完以后才轮到中置过滤器调用。 而中置拦截器可以修改 modelAndView。

成功调到中置过滤器 
ModelAndView就是最原始的Spring MVC返回值,如果使用了@ResponseBody注解或者其他注解改变返回值,那就没有ModelAndView了。

ModelAndView演示 :
首先定义jsp, 因为ModelAndView需要按照特定的规则去解析,需要有对应的jsp文件

重写中置过滤器

设置MVC 的jsp路径:

Controller 类:

调用结果:
URL: http://localhost:9090/user/queryUser?language=tw
页面:
URL: http://localhost:9090/user/queryUser?language=zh
页面:

第八步:前端控制器请求视图解析器(ViewResolver)去进行视图解析,并返回View
还是核心方法 doDispatch方法体中,在执行完中置过滤器以后,我们就开始进行视图渲染的方法调用了。

源码如下:
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,@Nullable Exception exception) throws Exception {boolean errorView = false;//如果异常不为空if (exception != null) {if (exception instanceof ModelAndViewDefiningException) {logger.debug("ModelAndViewDefiningException encountered", exception);mv = ((ModelAndViewDefiningException) exception).getModelAndView();}else {Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);mv = processHandlerException(request, response, handler, exception);errorView = (mv != null);}}// Did the handler return a view to render?//视图渲染,响应视图if (mv != null && !mv.wasCleared()) {render(mv, request, response);if (errorView) {WebUtils.clearErrorRequestAttributes(request);}}else {if (logger.isTraceEnabled()) {logger.trace("No view rendering, null ModelAndView returned.");}}if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {// Concurrent handling started during a forwardreturn;}//后置过滤器if (mappedHandler != null) {mappedHandler.triggerAfterCompletion(request, response, null);}}
核心方法就是 render(mv, request, response), 它负责视图的渲染与响应

获取到视图解析器:

进入方法以后,我们发现视图解析器会根据我们实际业务方法,生成各种各样的View,然后根据当前的方法获取到最优的View并进行返回,直到返回到前端控制器(DispatcherServlet)中.

我们根据上图中的步骤逐步进行源码解析:
1、创建视图View

调用 createView 方法创建新视图 View:

最终会创建视图View,并且对视图进行各种属性的设置,比如视图的URL:

2. 根据ContentType 以及 ViewName等很多的条件,选取最佳的 View

而ContentType 则是定义在jsp页面中的:

第九步:视图解析器像前端控制器返回View
这一步最简单,就是根据第八步选取的View一直进行返回,直到DispatcherServlet中。

第十步:前端控制器对视图进行渲染

核心方法:
@Overrideprotected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {//把响应数据设置到request对象中// Expose the model object as request attributes.exposeModelAsRequestAttributes(model, request);// Expose helpers as request attributes, if any.exposeHelpers(request);//获取到跳转的地址// Determine the path for the request dispatcher.String dispatcherPath = prepareForRendering(request, response);// Obtain a RequestDispatcher for the target resource (typically a JSP).RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);if (rd == null) {throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +"]: Check that the corresponding file exists within your web application archive!");}// If already included or response already committed, perform include, else forward.if (useInclude(request, response)) {response.setContentType(getContentType());if (logger.isDebugEnabled()) {logger.debug("Including [" + getUrl() + "]");}rd.include(request, response);}else {// Note: The forwarded resource is supposed to determine the content type itself.if (logger.isDebugEnabled()) {logger.debug("Forwarding to [" + getUrl() + "]");}rd.forward(request, response);}}
其实,就是一个forward请求转发到对应的 jsp 页面而已。
第十一步:前端控制器向用户响应结果
请求都转发到 jsp 页面了, 这一步确实不知道该说些什么了。 就此结束本篇。
后置过滤器:
后置过滤器是最后调用的,目的就是释放一些资源,总之就是最后干的事情。最终会调到我们自己定义的拦截器中的后置过滤器的方法中:

自定义的后置拦截器中的后置过滤器方法:

我们在使用Spring mvc的时候,经常会使用各种各样的注解进行参数的传递,比如 @RequestParam 、@PathVariable、@ModelAttribute、@CookieValue、@RequestHeader等各种各样的注解。下一篇会专门针对这些注解,挑出几个常用的进行分析。
相关文章:
Spring MVC 的调用(12)
目录 SpringMVC流程 源码分析 第一步:用户发起请求到前端控制器(DispatcherServlet) 第二步:前端控制器请求处理器映射器(HandlerMappering)去查找处理器(Handle):通过xml配置或者…...
死磕内存篇 --- JAVA进程和linux内存间的大小关系
运行个JAVA 用sleep去hold住 package org.hjb.test; public class TestOnly { public static void main(String[] args) { System.out.println("sleep .."); try { Thread.sleep(10000000); } catch (InterruptedException e) { e.printStackTrace(); } } } java -…...
信号完整性分析:关于传输线的三十个问题解答(三)
21.FR4 中 50 欧姆传输线的单位长度电感是多少?如果阻抗加倍怎么办?(What is the inductance per length of a 50-Ohm transmission line in FR4? What if the impedance doubles?) FR4 中的所有 50 欧姆传输线的单位长度电感约…...
Java基础:Stream流常用方法
获取Stream流的方式 java.util.stream.Stream 是Java 8新加入的流接口。(并不是一个函数式接口) 获取一个流非常简单,有以下几种常用的方式: 所有 Collection 集合都可通过 stream 默认方法获取流(顺序流)…...
ImageNet使用方法(细节)自用!
学习记录,自用。 1. 下载数据集 点击以下链接下载种子文件,然后使用迅雷进行下载,仅下载勾选的文件即可。 https://hyper.ai/datasets/4889/c107755f6de25ba43c190f37dd0168dbd1c0877e 2. 解压 找到下载好的ILSVRC2012_img_train.tar 和…...
C/C++外观模式解析:简化复杂子系统的高效方法
C外观模式揭秘:简化复杂子系统的高效方法 引言设计模式的重要性外观模式简介与应用场景外观模式在现代软件设计中的地位与价值 外观模式基本概念外观模式的定义与核心思想提供简单接口隐藏复杂子系统设计原则与外观模式的关系外观模式实现外观模式的UML图 外观模式的…...
追梦之旅【数据结构篇】——详解小白如何使用C语言实现堆数据结构
详解小白如何使用C语言实现堆数据结构 “痛”撕堆排序~😎 前言🙌什么是堆?堆的概念及结构 堆的性质:堆的实现堆向下调整算法画图分析:堆向下调整算法源代码分享:向下调整建小堆向下调整建大堆 堆向上调整算…...
cocoscreator性能优化4-Sprite颜色数据去除
前言 Sprite是游戏内容的一个基本组成元素,包括ui、道具、立绘等各种地方都会用到。大部分情况下美术会帮我们调好图片颜色,我们只要把图片直接放到游戏里就行了。Sprite默认的渲染顶点数据中包含了颜色数据,由于我们并不需要去修改颜色&…...
系统接口幂等性设计探究
前言: 刚开始工作的时候写了一个带UI页面的工具,需要设计登录功能,登录功能也很简单,输入用户名密码点击登录,触发后台查询并比对密码,如果登录成功则返回消息给前端,前端把消息弹出提示一下。…...
C learning_7
目录 1.for循环 1.虽然while循环和for循环本质上都可以实现循环,但是它们在使用方法和场合上还是有一些区别的。 2.while循环中存在循环的三个必须条件,但是由于风格的问题使得三个部分很可能偏离较远,这样 查找修改就不够集中和方便。所以…...
PageRank算法介绍
互联网上有数百亿个网页,可以分为这么几类:不含有用信息的,比如垃圾邮件;少数人比较感兴趣的,但范围不是很广的,比如个人博客、婚礼公告或家庭像册;很多人感兴趣的并且十分有用的,比…...
springboot+vue职称评审管理系统(源码+文档)
风定落花生,歌声逐流水,大家好我是风歌,混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的职称评审管理系统。项目源码请联系风歌,文末附上联系信息 。 目前有各类成品java毕设,需要请看文末联系方式 …...
腾讯云4核8G轻量服务器12M支持多少访客同时在线?并发数怎么算?
腾讯云轻量4核8G12M轻量应用服务器支持多少人同时在线?通用型-4核8G-180G-2000G,2000GB月流量,系统盘为180GB SSD盘,12M公网带宽,下载速度峰值为1536KB/s,即1.5M/秒,假设网站内页平均大小为60KB…...
图片英文翻译成中文转换器-中文翻译英文软件
您正在准备一份重要的英文资料或文件,但是您还不是很熟练地掌握英文,需要翻译才能完成您的任务吗?哪个软件能够免费把英文文档翻译成中文?让我们带您了解如何使用我们的翻译软件来免费翻译英文文档为中文。 我们的翻译软件是一款功…...
月薪10k和40k的程序员差距有多大?
程序员的薪资一直是大家关注的焦点,相较于其他行业,程序员的高薪也是有目共睹的,而不同等级的程序员处理问题的方式与他们的薪资直接挂钩。 接下来就一起看一下月薪10k、20k、30k、40k的程序员面对问题都是怎么处理的吧! 场景一 …...
gateway整合knife4j(微服务在线文档)
文章目录 knife4j 微服务整合一、微服务与单体项目文档整合的区别二、开始整合1. 搭建一个父子maven模块的微服务,并引入gateway2.开始整合文档 总结 knife4j 微服务整合 由于单个服务的knife4j 整合之前已经写过了,那么由于效果比较好,然后微服务的项目中也想引入,所以开始微…...
ASP.NET 记录 HttpRequest HttpResponse HttpServerUtility
纯属个人记录,会有错误 HttpRequest Browser是获取客户端浏览器的信息 Cookies是获取客户端的Cookies QueryString是获取客户端提交的数据 ServerVariables是获取服务器端或客户端的环境变量信息 Browser 语法格式: Request.Browser[“浏览器特性名”] 常见的特性名 名称说…...
Python 人工智能:11~15
原文:Artificial Intelligence with Python 协议:CC BY-NC-SA 4.0 译者:飞龙 本文来自【ApacheCN 深度学习 译文集】,采用译后编辑(MTPE)流程来尽可能提升效率。 不要担心自己的形象,只关心如何…...
辉煌优配|军工板块逆市上涨,16只概念股已披露一季度业绩预喜
今日,军工股逆市上涨。 4月21日,A股三大股指低开低走,半导体、AI使用、信创工业、软件等科技属性概念领跌,国防军工、食品饮料和电力设备等板块上涨。 工业互联网中心工业规模超1.2万亿元 据央视新闻报道,本年是《工业…...
看板与 Scrum:有什么区别?
看板和Scrum是项目管理方法论,以小增量完成项目任务并强调持续改进。但是他们用来实现这些目标的过程是不同的。看板以可视化任务和连续流程为中心,而Scrum更多是关于为每个交付周期实施时间表和分配设定角色。 在看板和Scrum之间做出选择并不总是必要…...
用MNN实现手机端AI绘画:Android Studio集成与模型量化实战
用MNN实现手机端AI绘画:Android Studio集成与模型量化实战 移动端AI应用正在经历爆发式增长,其中AI绘画因其创意性和实用性成为开发者关注的热点。本文将手把手教你如何通过阿里开源的MNN框架,在Android应用中实现高性能的AI绘画功能。不同于…...
YOLOE官版镜像部署指南:从环境配置到实战推理全流程
YOLOE官版镜像部署指南:从环境配置到实战推理全流程 1. 环境准备与快速部署 1.1 系统要求与准备工作 在开始部署YOLOE官版镜像前,请确保您的系统满足以下基本要求: 操作系统:推荐使用Ubuntu 20.04/22.04或CentOS 7/8GPU支持&a…...
高效解决Magpie插件更新难题:完全掌握图像增强功能升级指南
高效解决Magpie插件更新难题:完全掌握图像增强功能升级指南 【免费下载链接】Magpie An all-purpose window upscaler for Windows 10/11. 项目地址: https://gitcode.com/gh_mirrors/mag/Magpie 识别插件更新需求:为何及时升级至关重要 在使用M…...
别再只会用滑动平均了!用Python从零实现数字陷波器,精准滤除50Hz工频干扰
从零构建Python数字陷波器:精准滤除50Hz工频干扰的工程实践 当你在深夜调试一个心爱的传感器项目时,突然发现采集到的数据波形上叠加了一个顽固的50Hz正弦波——这种经历想必不少硬件开发者都深有体会。工频干扰就像电子世界中的背景噪音,无…...
HDC1000温湿度传感器原理与嵌入式实战指南
1. 项目概述Grove - Temperature & Humidity Sensor (HDC1000) 是 Seeed Studio 推出的一款基于德州仪器(Texas Instruments)HDC1000 芯片的数字温湿度传感器模块。该模块采用标准 Grove 接口,支持 IC 总线通信,专为嵌入式系统…...
哔哩哔哩API神器bilibili-api:Python开发者的终极爬虫工具指南
哔哩哔哩API神器bilibili-api:Python开发者的终极爬虫工具指南 【免费下载链接】bilibili-api 哔哩哔哩常用API调用。支持视频、番剧、用户、频道、音频等功能。原仓库地址:https://github.com/MoyuScript/bilibili-api 项目地址: https://gitcode.com…...
指针的使用
指针基本用法C语言中使用指针可以1.程序简洁,紧凑,高效2.有效的表达复杂的数据结构3.动态分配内存4.得到多余一个的函数返回值5.编译或函数调用时为其分配内存单元6.变量是对程序中数据存储空间的抽象指针的感念在C语言中,内存单元的地址&…...
基于SSM + Vue的二手物品交易网站系统(角色:用户、管理员)
文章目录前言一、详细操作演示视频二、具体实现截图三、技术栈1.前端-Vue.js2.后端-SpringBoot3.数据库-MySQL4.系统架构-B/S四、系统测试1.系统测试概述2.系统功能测试3.系统测试结论五、项目代码参考六、数据库代码参考七、项目论文示例结语前言 💛博主介绍&#…...
Python AI部署效能革命(Cuvil编译器内核逆向工程实录)
第一章:Python AI部署效能革命的底层驱动力Python 已成为 AI 模型开发的事实标准,但其在生产环境中的部署效能长期受限于解释执行、全局解释器锁(GIL)及内存管理机制。近年来,一场静默却深刻的效能革命正在重塑 Python…...
使用Yolo 11进行定制化图像识别全流程
全流程预览 Label Studio标注 → 导出YOLO格式 → 编写data.yaml → 拆分数据集 → 模型训练 → 预测部署步骤工具/技术产出物数据标注Label Studio标注好的图片数据导出YOLO with imagesimages/ labels/配置文件data.yaml数据集配置数据拆分Python脚本train/val/test模型训练…...
