SpringMVC组件原理剖析

news2024/12/23 18:09:17

文章目录

  • SpringMVC组件原理剖析
  • 一、 前端控制器初始化
    • 1.1 初始化SpringMVC容器
    • 1.2 注册了 SpringMVC的 九大组件
    • 1.3 处理器映射器初始化细节
  • 二、前端控制器执行主流程
    • 2.1 定位doDispatcher方法
    • 2.2 验证HandlerExecutionChain
    • 2.3 HandlerAdapter执行目标方法

SpringMVC组件原理剖析

主要剖析DispatcherServlet(前端控制器 )初始化的过程,还有DispatcherServlet执行主流程

一、 前端控制器初始化

DispatcherServlet初始化做了两件事情

  • 获得了一个 SpringMVC 的 ApplicationContext容器
  • 注册了 SpringMVC的 九大组件

image-20230613111658664

1.1 初始化SpringMVC容器

前端控制器DispatcherServlet是SpringMVC的入口,也是SpringMVC的大脑,主流程的工作都是在此完成的

DispatcherServlet 本质是个Servlet,当配置了 load-on-startup 时,会在服务器启动时就执行创建和执行初始化init方法,每次请求都会执行service方法

找一下init方法,发现DispatcherServlet类中没有,就去找他爹FrameworkServlet类,爹也没有,就一直找,直到HttpServletBean类中有一个init方法

找到init方法,看到调用了一个initServletBean方法,点进去看看

image-20230613112755961

initServletBean方法如下所示,发现啥也没有,说明是子类实现的,也就是FrameworkServlet类

image-20230613112837298

在FrameworkServlet类中找到initServletBean方法,然后发现有下面一条语句,获取web环境下的Spring容器

image-20230613113105857

点进去看看,发现创建了一个Spring的容器

image-20230613113321181

还是此方法,再往下看,把Spring容器的引用作为参数放入了下面的方法中

传入Spring容器有什么作用?

​ 将Spring容器的引用设置为SpringMVC的一个属性,通过这个地方体现出一个父子容器的关系

image-20230613113849406

父子容器有什么作用?

当SpringMVC在获取Bean的时候,首先会从自己的容器中获取,如果自己容器没有的话会使用parent找到父容器,也就是Spring容器,再从里面看看有没有对应的Bean

Spring中能不能获取到SpringMVC中的Bean?

不能,因为SpringMVC是子容器

image-20230613113637730

1.2 注册了 SpringMVC的 九大组件

没太屡明白141-SpringMVC框架-组件原理剖析-前端控制器初始化-注册九大组件_哔哩哔哩_bilibili

当我们把SpringMVC容器创建出来后,会执行到下面标红的语句

configureAndRefreshWebApplicationContext,配置和刷新SpringMVC容器

image-20230613142735169

继续往下走,如果没有的话就进行创建

image-20230613142943775

点进上图中的方法,一直点到下图

发现也有configureAndRefreshWebApplicationContext方法的调用

image-20230613143105849

经过上面两个过程,不管是新创建的SpringMVC容器还是只有就有的SpringMVC容器,都会执行configureAndRefreshWebApplicationContext方法




点进方法configureAndRefreshWebApplicationContext

发现此方法中有一个refresh方法

image-20230613144309374

点进refresh方法,发现最后调用了一个finishRefresh方法,完成刷新

image-20230613144532690

点进去finishRefresh方法看一下,此方法中发布了一个事件

我们设置上这个事件,事件对应的监听都会执行

image-20230613144648104




此时再回到FrameworkServlet类

找到下面这个方法,是一个监听器,监听的东西就是泛型ContextRefreshedEvent,也就是我们发布的事件的类型(上图)

这段FrameworkServlet.this.onApplicationEvent(event);代码会执行

为什么会被执行?

在另一个地方会发布事件

image-20230613145827973

​ 其中在FrameworkServlet类中有一个监听器就是监听上面的事件的

​ 监听到后这段FrameworkServlet.this.onApplicationEvent(event);代码会执行

image-20230613145140415

看一下onApplicationEvent方法,里面有一个onRefresh方法,

image-20230613150017954

看一下onRefresh方法,但是内部什么也没写,说明是子类进行实现

image-20230613150131943

看一下FrameworkServlet类的子类DispatcherServlet类中的onRefresh方法

image-20230613150304968

最后完成注册SpringMVC九大组件

image-20230613142408326

1.3 处理器映射器初始化细节

注册了九个组件,我们可以选一个我们比较熟悉的initHandlerMappings进行查看

这一步的操作就是看看Spring容器之中有没有类型是HandlerMapping的组件,如果有的话,matchingBeans参数就不是空了

image-20230613152129792

那此时就进不去下面这个if判断了,就不会加载默认配置文件中的组件

image-20230613152446708

打断点之后发现,为什么会有四个HandlerMapping类型的参数?

image-20230613154805606

我们之前配置了一个注解@EnableWebMVC

image-20230613154946059

或者说spring-mvc.xml文件中的

image-20230613155036478

MVC注解驱动的作用有很多,会帮我们向SpringMVC中注入一些组件,其中HandlerMapping就是在这个地方注册的

上面这几句话的操作都是@EnableWebMVC注解帮我们完成的

如果我们把@EnableWebMVC注解注释掉调后,matchingBeans参数的大小就是0,最终会加载默认配置文件中的组件,就是下图框起来的

image-20230613155451134

四个HandlerMapping类型的组件,我们点开看一个

但是我们只手动配置了一个拦截器,为什么会显示两个呢?

Spring本身提供了两个

image-20230613160242152

再看一下MappingRegistery参数

image-20230613160549789

随便点进去一个看看

image-20230613160706069

二、前端控制器执行主流程

当服务器启动时,DispatcherServlet 会执行初始化操作,接下来,每次访问都会执行service方法,我们先宏观的看一下执行流程,在去研究源码和组件执行细节

image-20230613162624565

2.1 定位doDispatcher方法

在DispatcherServlet类中找service方法,但是没有,那就找他爹FrameworkServlet类,发现有Service方法,但是此方法不是最原生的,最原生的方法是ServletRequest参数

image-20230613163345862

再找FrameworkServlet类的爹HttpServletBean类,但是没有Service方法

再找HttpServlet类,发现有Service方法,并且是原生的(参数前面没有http)

image-20230613164011177

上图中的service方法又调用了下面的service方法(这个service进行重载了)

内部根据请求方式,看看是调用dopost还是doGet

image-20230613164605139

我们可以看一下doPost方法,但是此方法左边有一个小标志,说明已经被子类覆盖了

image-20230613164726841

点一下小标志,然后进入到FrameworkServlet类中,并看到doPost方法

image-20230613164845841

doPost方法中又调用了一个processRequest方法

image-20230613164951983

processRequest方法中又调用了doService方法,但是我们发现doService是一个抽象方法,我们需要找到对应的实现

image-20230613165032250

看一下doService抽象方法的具体实现,就到了DispatcherServlet类

doService方法中调用了另外一个方法doDispatch

image-20230613165654298

看一下doDispatch方法,最核心的主流程就在这里

2.2 验证HandlerExecutionChain

从DispatcherServlet类中的doDispatch开始找

此方法中有一个参数HandlerExecutionChain,这个参数内部包括Interceptor、目标对象

此方法中 this.getHandler(processedRequest)的调用对HandlerExecutionChain的参数mappedHandler进行初始化

image-20230613171135917

我们看一下getHandler方法是什么,如下所示

如果参数handlerMappings不是空,就对其进行循环

image-20230613171243796

那handlerMappings参数是什么呢?点进去看看

发现就是最终装HandlerMapping的集合

HandlerMapping的的填充在前面1.3进行讲解了

image-20230613171338114

再回到getHandler方法,其中遍历集合的时候又调用了mapping.getHandler(request)方法

image-20230613171718008

我们再看一下mapping.getHandler方法是干嘛的

image-20230613171802289

找对应的实现,选择第一个

image-20230613171831955

然后再这个类中对应的实现又调用了getHandlerExecutionChain方法

image-20230613172046527

再看一下getHandlerExecutionChain方法

image-20230613172423819

最终这个chain参数就返回到doDispatcher方法,如下标红的位置

image-20230613173020004

上面的过程,就是下图中标红的地方

image-20230613173105376

2.3 HandlerAdapter执行目标方法

doDispatcher方法还没有完成,继续往下看

再往下走会调用getHandlerAdapter方法

image-20230613174053137

继续往下走,会执行拦截器的preHandle前置方法

image-20230613174353969

执行目标方法

image-20230613174432492

执行后置方法

image-20230613174446037

然后我们发现执行前置方法和后置方法的时候并不是HandlerAdapter对象执行的

但是执行目标方法的时候是HandlerAdapter对象执行的

然后我们可以看一下handle方法,执行目标方法

发现没有实现,我们看一下子类的实现

image-20230613175007402

选择下图中的第一个

image-20230613175047564

顺着截图向下走

image-20230613175117213

image-20230613175141977

最终到了下面这个地方

image-20230613175204955

继续执行,会进入到下面标红的方法中

image-20230613175458582

继续点进去

image-20230613175547655

image-20230613175625668

到了下面这个地方,看一下参数

image-20230613175727043

上图的参数就是在我们访问时对应的参数

image-20230613175754331

再点进doInvoke方法

image-20230613175842058

然后发现了执行method.invoke(this.getBean(), args)方法

反射代码,最终通过反射执行目标方法

image-20230613180008739

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

64位和32位相比优势是什么(一)

前置知识:程序是如何执行的? 一道常规的面试题:相比 32 位,64 位的优势是什么? 面试官考察这种类型的问题,主要是想看求职者是否有扎实的计算机基础,同时想知道求职者在工作中是否充满好奇&am…

路径规划算法:基于未来搜索优化的路径规划算法- 附代码

路径规划算法:基于未来搜索优化的路径规划算法- 附代码 文章目录 路径规划算法:基于未来搜索优化的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要:本文主要介绍利用智能优化…

【硬件专题】案例:怎么通过元件丝印信息反查芯片

今天同事和昨天因为工作上的原因,问我一个问题,就是怎么通过丝印(Marking code)知道用的是什么芯片。以下列举几个方法: 凭经验 比如昨天给出的一张图片,看图片是比较模糊的。但是根据之前的使用,看芯片LOGO很明显是ST的,然后看上面的型号是STM**F1*VCT*,那么…

Go语言环境安装和程序结构

Go语言环境安装和程序结构 1、Go环境安装 Go安装包下载地址为: https://golang.org/dl/ https://golang.google.cn/dl/ 1.1 Windows下的安装 Windows 下可以使用.msi 后缀的安装包来安装,我这里下载的安装包是 go1.18.4.windows-amd64.msi&#xf…

C\C++ Thread-

文章作者:里海 来源网站:https://blog.csdn.net/WangPaiFeiXingYuan 简介 说明 时间 c语言的时间处理:time.h 获取从1970年1月1日到当前经过的秒数: long t0 time(NULL); 让程序暂停3秒: sleep(3); 当前时间的3秒后&#x…

Pycharm 通过 SVN 直接管理控制代码,原来这么方便又高级!

做自动化测试的小伙伴都知道,代码不会只放到本地管理,需要托管到远端进行管理! 一方面,发布在不同的电脑上进行同步开发,不需要用U盘拷来拷去;另外一方面,可以轻松找回代码,避免本地…

【UE】玻璃材质

效果 步骤 1. 新建一个材质,这里命名为“M_GLASS” 双击打开“M_GLASS”,左下角混合模式设置为半透明 光照模式设置为表面前向着色 将基础颜色提升为参数 同样还需提升为参数的有“高光度”、“粗糙度”、“不透明度”、“折射” 设置高光度的默认值和最…

【软件测试面试】几句话让面试官再掏3K,轻松应对测试面试...

目录:导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜) 前言 面试官&#xff1…

查看网卡中断在哪个CPU核处理

本文目录 1、查看CPU占用率2、查看系统的中断处理在各个CPU核间的分布情况3、查看用于处理网卡中断的CPU核4、修改用于处理网卡中断的CPU核 在默认的情况下Linux默认一个网卡的中断操作都在一个CPU核里处理,在大数据量或者说是对网卡的收发包性能PPS进行测试时&…

2023-06-13:统计高并发网站每个网页每天的 UV 数据,结合Redis你会如何实现?

2023-06-13:统计高并发网站每个网页每天的 UV 数据,结合Redis你会如何实现? 答案2023-06-13: 选用方案:HyperLogLog 如果统计 PV (页面浏览量)那非常好办,可以考虑为每个网页创建…

像医生一样的大规模AI模型

目录 华佗:BenTsao相关工作华佗模型实验 HuatuoGPT动机解决方案混合数据的SFT基于AI反馈的RL 医学中的LLM指令微调 华佗:BenTsao 大型语言模型(LLM),如LLaMA模型,已经证明了它们在各种通用领域自然语言处理…

《代码随想录》(8)反转链表

LeeCode题号: 206 【题目描述】 给你单链表的头节点 head,请你反转链表,并返回反转后的链表。 【示例1】 输入:head [1,2,3,4,5] 输出:[5,4,3,2,1]【示例2】 输入:head [1,2] 输出:[2,1]【示…

实现Vue3和UE5.2进行通信(Pixel Streaming)

文章目录 1. 从UE5.2到前端页面的通信1.1 编写蓝图脚本1.2 编写前端的响应函数1.3 功能验证 2. 从Vue3到UE5.2的信息发送2.1 UE5.2蓝图的设计2.2 前端发送消息功能的实现2.3 功能验证 3. 参考资源 这篇文章简单讲解一下如何实现vue3和UE5进行数据的通信。 如果有同学还不清楚如…

3.数据操作

SQL句子中语法格式提示: 1.中括号([])中的内容为可选项; 2.[,...]表示,前面的内容可重复; 3.大括号({})和竖线(|)表示选择项,在选择…

Jenkins集成钉钉通知插件的具体步骤怎么做你知道吗?

最近公司要求工作务必使用钉钉,其他聊天软件不再用于工作沟通了。虽然很抓狂,但是上面的决定不可违逆,只好转战钉钉。虽然强制使用钉钉挺令人反感的,但阿里在这款软件上确实下了些功夫,比如jenkins集成钉钉通知插件后&…

(原创)自定义控件:写一个瀑布流效果

效果展示 最近要业务中需要做一个瀑布流的效果,按理说正常的瀑布流网上已经有很多解决方案了。 但我还是想自己尝试写一下。 又因为这块要求有一点特殊,下面大概讲下需求: 首先子元素的对方肯定还是和其他瀑布流一样,按照子View的…

对任意给定的NFA M进行确定化操作(附详细注释)

对任意给定的NFA M进行确定化操作&#xff08;附详细注释&#xff09; DFA实体类 package Beans;import java.util.List;public class DFA {private List<Integer> K; // 状态集private char[] letters; // 字母表private String[][] f; // 转换函数priva…

每天五分钟机器学习:梯度下降的学习率太大或太小会有什么问题?

本文重点 我们前面学习了梯度下降算法,其中有一个重要的参数就是学习率。在使用梯度下降算法时,学习率是一个非常重要的参数。学习率的大小会直接影响梯度下降算法的收敛速度和精度。如果学习率太大或太小,都会对梯度下降算法的表现产生负面影响。 学习率太大的影响 学习率…

Debezium系列之:记录一次生产环境SQLServer数据库删除日志文件造成debezium connector数据不采集的解决方法

Debezium系列之:记录一次生产环境SQLServer数据库删除日志文件造成debezium connector数据不采集的解决方法 一、背景二、快速定位问题三、详细的解决步骤四、确认debezium connector恢复对数据库的数据采集五、经验总结一、背景 SQLServer数据库的日志把磁盘打满了,需要删除…

空间计算时代下,中国能否诞生下一个“苹果”?

“one more thing&#xff01;” 6月6日的WWDC大会上&#xff0c;苹果CEO库克激动地喊出这句乔布斯的口头禅。随后&#xff0c;苹果的Vision Pro头显产品正式亮相&#xff0c;库克形容它是“革命性产品”“开启空间计算时代”。 当一个类似滑雪镜的头显设备出现在屏幕&#x…