chapter32_SpringMVC与DispatcherServlet
一、简介
从本章节开始进入SpringMVC的学习,SpringMVC最重要的类就是DispatcherServlet
DispatcherServlet的本质是一个Servlet,回顾一下Servlet
- JavaWeb就是基于Servlet的
- Servlet接口有5个方法
- Servlet实现类是HttpServlet,自定义的Servlet需要继承HttpServlet,重写service方法
- 使用SpringMVC后,DispatcherServlet就是唯一的Servlet,所有的请求由他分发
二、目标
- 手写SpringMVC的核心类DispatcherServlet
- 通过SCI与Tomcat对接
三、手写DispatcherServlet
新建抽象类FrameworkServlet,继承HttpServlet,它的功能是维护WebApplicationContext容器
- 父容器在Spring对接SCI的时候刷新
- 子容器在在DispatcherServlet的init方法刷新
/*** Spring 对 Servlet 的抽象实现类,负责管理 Web ioc 容器** @Author 孤风雪影* @Email gitee.com/efairy520* @Date 2025/4/15 3:24* @Version 1.0*/
public abstract class FrameworkServlet extends HttpServlet {// 子容器private ApplicationContext webApplicationContext;public FrameworkServlet(ApplicationContext webApplicationContext) {this.webApplicationContext = webApplicationContext;}@Overridepublic void init() {initServletContext();}private void initServletContext() {ApplicationContext rootContext = (ApplicationContext) getServletContext().getAttribute(WebApplicationContext.ROOT_NAME);AbstractRefreshableWebApplicationContext cwc = null;// 在springboot场景下会根据当前存在类创建不同ioc,在boot下直接不管if (this.webApplicationContext != null) {if (!(this.webApplicationContext instanceof AnnotationConfigApplicationContext)) {cwc = (AbstractRefreshableWebApplicationContext) this.webApplicationContext;if (cwc.getParent() == null) {cwc.setParent(rootContext);}if (!cwc.isActive()) {cwc.refresh();}cwc.setServletConfig(getServletConfig());cwc.setServletContext(getServletContext());}onRefresh(webApplicationContext);}}protected abstract void onRefresh(ApplicationContext applicationContext);
}
新建DispatcherServlet
- 作为前置处理器
- 实现service方法
public class DispatcherServlet extends FrameworkServlet {public DispatcherServlet(WebApplicationContext webApplicationContext) {super(webApplicationContext);}@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("DispatcherServlet的service方法调用");}// 组件初始化,Servlet的init方法调用@Overrideprotected void onRefresh(ApplicationContext applicationContext) {}
}
四、通过SCI与Tomcat对接
对接SCI,需要新建一个接口WebApplicationInitializer,所有实现了这个接口的类,容器启动的时候会自动调用其onStartup方法
public interface WebApplicationInitializer {/*** 所有实现了这个接口的类,容器启动的时候会自动调用其 onStartup 方法* @param servletContext Tomcat会传入servletContext*/void onStartup(ServletContext servletContext);
}
新建一个抽象类AbstractDispatcherServletInitializer,实现WebApplicationInitializer接口,实现onStartup方法
/*** 对接SCI,实现onStartup方法** @Author 孤风雪影* @Email gitee.com/efairy520* @Date 2025/4/15 3:29* @Version 1.0*/
public abstract class AbstractDispatcherServletInitializer implements WebApplicationInitializer {public static final String DEFAULT_SERVLET_NAME = "dispatcher";public static final String DEFAULT_FILTER_NAME = "filters";public static final int M = 1024 * 1024;@Overridepublic void onStartup(ServletContext servletContext) {// 创建父容器final AbstractApplicationContext rootApplicationContext = createRootApplicationContext();// 父容器放入servletContextservletContext.setAttribute(WebApplicationContext.ROOT_NAME, rootApplicationContext);// 刷新父容器(通过register配置类,所以需要手动刷新) -> 在源码中是通过事件进行refreshrootApplicationContext.refresh();final WebApplicationContext webApplicationContext = createWebApplicationContext();// 创建DispatcherServletfinal DispatcherServlet dispatcherServlet = new DispatcherServlet(webApplicationContext);ServletRegistration.Dynamic dynamic = servletContext.addServlet(DEFAULT_SERVLET_NAME, dispatcherServlet);// 配置文件信息dynamic.setLoadOnStartup(1);final MultipartConfigElement configElement = new MultipartConfigElement(null, 5 * M, 5 * M, 5);dynamic.setMultipartConfig(configElement);dynamic.addMapping(getMappings());final Filter[] filters = getFilters();if (!ObjectUtil.isEmpty(filters)) {for (Filter filter : filters) {servletContext.addFilter(DEFAULT_FILTER_NAME, filter);}}}// 过滤器protected abstract Filter[] getFilters();// 映射器protected String[] getMappings() {return new String[]{"/"};}// 创建父容器,管理Service,Dao对象protected abstract AbstractApplicationContext createRootApplicationContext();// 创建子容器,管理Controller对象protected abstract WebApplicationContext createWebApplicationContext();}
建抽象类AbstractAnnotationConfigDispatcherServletInitializer,继承AbstractDispatcherServletInitializer,实现创建父子容器的方法
- 用户需要实现这个类,提供配置类
/*** 对接SCI,实现创建父子容器的方法** @Author 孤风雪影* @Email gitee.com/efairy520* @Date 2025/4/15 4:01* @Version 1.0*/
public abstract class AbstractAnnotationConfigDispatcherServletInitializer extends AbstractDispatcherServletInitializer {@Overrideprotected AbstractApplicationContext createRootApplicationContext() {final Class<?> rootConfigClass = getRootConfigClass();if (ObjectUtil.isNotNull(rootConfigClass)) {final AnnotationConfigApplicationContext rootContext = new AnnotationConfigApplicationContext();rootContext.register(rootConfigClass);return rootContext;}return null;}@Overrideprotected WebApplicationContext createWebApplicationContext() {final Class<?> webConfigClass = getWebConfigClass();if (ObjectUtil.isNotNull(webConfigClass)) {final AnnotationConfigWebApplicationContext webContext = new AnnotationConfigWebApplicationContext();webContext.register(webConfigClass);return webContext;}return null;}// 下面两个方法,由用户实现protected abstract Class<?> getRootConfigClass();protected abstract Class<?> getWebConfigClass();
}
新建SpringServletContainerInitializer类, 它负责spring与SCI的对接
- 它的onStartup方法由Tomcat调用
- 最终调用用户实现的WebApplicationInitializer类的onStartup
/*** Spring与SCI对接的类* 1.需要实现ServletContainerInitializer* 2.扫描 @HandlesTypes 指定的类** @Author 孤风雪影* @Email gitee.com/efairy520* @Date 2025/4/15 4:13* @Version 1.0*/
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {// 此方法由Tomcat调用@Overridepublic void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException {if (webAppInitializerClasses.size() != 0) {final List<WebApplicationInitializer> initializers = new ArrayList<>(webAppInitializerClasses.size());// 排除接口和抽象类for (Class<?> webAppInitializerClass : webAppInitializerClasses) {if (!webAppInitializerClass.isInterface() && !Modifier.isAbstract(webAppInitializerClass.getModifiers()) &&WebApplicationInitializer.class.isAssignableFrom(webAppInitializerClass)) {try {initializers.add((WebApplicationInitializer)ReflectUtil.getConstructor(webAppInitializerClass).newInstance());} catch (Throwable e) {e.printStackTrace();}}}for (WebApplicationInitializer initializer : initializers) {initializer.onStartup(servletContext);}}}
}
配置SPI


五、测试
install一下chapter32模块

在tomcat9源码中导入pom依赖
<dependency><groupId>cn.shopifymall</groupId><artifactId>splendid-spring-chapter-32</artifactId><version>1.0-SNAPSHOT</version>
</dependency>
提供配置类
/*** @Author 孤风雪影* @Email gitee.com/efairy520* @Date 2025/4/15 5:16* @Version 1.0*/
@Configuration
@ComponentScan("cn.shopifymall.tomcat")
public class AppConfig {
}
提供用户类
/*** 此类为用户类,实现了 WebApplicationInitializer** @Author 孤风雪影* @Email gitee.com/efairy520* @Date 2025/4/15 5:17* @Version 1.0*/
public class QuickStart extends AbstractAnnotationConfigDispatcherServletInitializer {@Overrideprotected Filter[] getFilters() {return new Filter[0];}@Overrideprotected Class<?> getRootConfigClass() {return AppConfig.class;}@Overrideprotected Class<?> getWebConfigClass() {return AppConfig.class;}
}
运行Tomcat9的Bootstrap里面的main方法
- 首先启动Tomcat
- 识别到Pom依赖里面通过SPI注册的SpringServletContainerInitializer类,发现它是一个SCI
- 扫描到所有的WebApplicationInitializer类型的用户类
- 调用里面的onStartup方法,根据用户类提供的配置类,创建父子容器,并初始化DispatcherServlet
- Tomcat启动完成,开始接收用户请求

启动后,访问任意接口
- http://localhost:18080/
四月 16, 2025 3:02:26 上午 org.apache.catalina.startup.Catalina start
DispatcherServlet的service方法调用
相关文章:
chapter32_SpringMVC与DispatcherServlet
一、简介 从本章节开始进入SpringMVC的学习,SpringMVC最重要的类就是DispatcherServlet DispatcherServlet的本质是一个Servlet,回顾一下Servlet JavaWeb就是基于Servlet的Servlet接口有5个方法Servlet实现类是HttpServlet,自定义的Servle…...
构建批量论文格式修改系统:从内容识别到自动化处理
在学术研究和论文管理中,自动化处理论文格式是一个极具挑战性但非常有价值的任务。无论是提取论文的关键信息,还是批量修改格式,都需要一个强大的内容识别系统作为基础。本文将结合两份代码(paper_parser.py 和 paper_analyzer.py),深入分析它们如何实现论文内容的识别,…...
spring security解析
Spring Security 中文文档 :: Spring Security Reference 1. 密码存储 最早是明文存储,但是攻击者获得数据库的数据后就能得到用户密码。 于是将密码单向hash后存储,然后攻击者利用彩虹表(算法高级(23)-彩虹表&…...
STM32单片机C语言
1、stdint.h简介 stdint.h 是从 C99 中引进的一个标准 C 库的文件 路径:D:\MDK5.34\ARM\ARMCC\include 大家都统一使用一样的标准,这样方便移植 配置MDK支持C99 位操作 如何给寄存器某个值赋值 举个例子:uint32_t temp 0; 宏定义 带参…...
多模态融合(十一): SwinFusion——武汉大学马佳义团队(二)
目录 一.摘要 二. Introduction 三. Related Work A. 特定任务图像融合方法 B. 通用图像融合方法 C. 视觉 Transformer 四.方法 A. 整体框架 B. 损失函数 C.解析 1. 整体框架 2. 特征提取 3. 注意力引导的跨域融合 五. 实验结果与讨论 A. 实验配置 B. 实现…...
IDEA202403常用快捷键【持续更新】
文章目录 一、全局搜索二、美化格式三、替换四、Git提交五、代码移动六、调试运行 在使用IDEA进行程序开发,快捷键会让这个过程更加酸爽,下面记录各种快捷键的功能。 一、全局搜索 快捷键功能说明Shift Shift全局搜索Ctrl N搜索Java类 二、美化格式 …...
从 LabelImg 到 Label Studio!AI 数据标注神器升级,Web 版真香
视频讲解: 从 LabelImg 到 Label Studio!AI 数据标注神器升级,Web 版真香 Label Studio 支持图像、文本、音频、视频、时间序列等多类型数据标注,覆盖计算机视觉(目标检测、语义分割)、自然语言处理&#x…...
【ESP32】ESP-IDF开发 | 低功耗蓝牙开发 | GAP协议 + 设备扫描例程
1. 简介 1.1 GAP协议 GAP(General Access Protocol),全称通用访问协议,它定义了低功耗蓝牙设备的发现流程,设备管理和设备连接的建立。 低功耗蓝牙设备定义了4种角色: 广播者(Broadcaster&…...
网络开发基础(游戏)之 Socket API
Socket简介 Socket (套接字)是网络编程的基础,在 C# 中通过 System.Net.Sockets 命名空间提供了一套完整的 API 来实现网络通信。 网络上的两个程序通过一个双向的通信连接实现数据交换, 这个连接的一端称为一个Socket。 一个Socket包含了进行网络通信必…...
行为审计软件:企业合规与内部监控的数字守门人
在当今高度数字化的商业环境中,企业运营产生的电子数据呈指数级增长,员工行为也日益复杂多样。行为审计软件应运而生,成为现代企业管理不可或缺的工具。这类软件通过系统化记录、分析和报告组织内部用户活动,帮助企业管理风险、确…...
bat脚本转换为EXE应用程序文件
很多时候,我们使用电脑时会编辑bat脚本文件 很多时候,我们制作的玩笑,病毒也会使用这个格式. 但这个格式也有很多缺点 1,如果是需要管理员运行的程序,需要费劲的自己使用管理员身份运行 2,文件并不为大家所熟知,认同度不高 3,可以非常轻松的看到原代…...
细说STM32单片机FreeRTOS任务管理API函数vTaskList()的使用方法
目录 一、函数vTaskList() 1、 函数说明 2、返回的字符串表格说明 3、函数的使用方法 二、 vTaskList()的应用示例 1、示例功能、项目设置 2、软件设计 (1)main.c (2)freertos.c (3)FreeRTOSConf…...
DNS主从同步
安装软件 主配置中完成DNS解析:192.168.131.134 [rootlocalhost ~]# mount /dev/sr0 /mnt [rootlocalhost ~]# vim /etc/yum.repos.d/myrepo.repo [base] namebase baseurl/mnt/BaseOS gpgchcek0 enable1 [base2] namebase2 baseurl/mnt/AppStream gpgchcek0 enab…...
双指针算法(部分例题解析)
快慢指针左右指针 前言 双指针,它通过设置两个指针来遍历数据,从而实现高效的查找、排序、去重等操作。双指针算法的核心在于通过合理地移动这两个指针,减少不必要的遍历,提高算法的效率。 283. 移动零 - 力扣(LeetCo…...
解决Windows打印问题的集成软件
家里或公司电脑经常为连不上打印机而烦恼,今天给大家推荐一款修复打印工具,该工具是采用易语言开发的集成化打印机故障修复软件,专为解决 Windows 系统(含 32/64 位 Windows 7/10/11)中因权限配置、服务异常、补丁缺失…...
神经网络模型应用到机器学习时的难点
虽然神经网络具有非常强的表达能力,但是当应用神经网络模型到机器学习时依然存在一些难点问题。主要分为两大类: 优化问题:深度神经网络的优化十分困难。 首先,神经网络的损失函数是一个非凸函数,找到全局最优解通常比较困难。 …...
警惕阿里云中的yum update操作不当导致:/sbin/init被清空导致Linux无法正常启动
由于使用阿里云进行部署测试,因而会对yum update进行操作,这两天更新了systemd-239-82.0.3.4.al8.2.x86_64,但存在报错,然后进行yum history undo和清空yum cache,但出现操作Linux命令行无效。具体来说,几个…...
关系型数据库MYSQL(续)
目录 三.MySQL事务原理分析 1.事务是什么? 2.执行事务的目的是什么? 3.事务是由什么组成的? 4.事务的特征是什么? 5.事务控制语句 6.ACID特性 6.1原子性(A) 6.2隔离性(I) …...
WInform当今技术特性分析
Windows Forms (WinForms) 技术特性分析 引言 Windows Forms (WinForms) 作为微软最早推出的基于.NET的图形用户界面开发框架,已经存在了20多年。在如今充满了各种现代UI框架的软件开发生态系统中,WinForms仍然保持着其独特的地位。本文将深入分析WinF…...
day46——两数之和-输入有序数组(LeetCode-167)
题目描述 给你一个下标从 1 开始的整数数组 numbers ,该数组已按 非递减顺序排列 ,请你从数组中找出满足相加之和等于目标数 target 的两个数。如果设这两个数分别是 numbers[index1] 和 numbers[index2] ,则 1 < index1 < index2 &l…...
Python 一等函数( 把函数视作对象)
把函数视作对象 示例 5-1 中的控制台会话表明,Python 函数是对象。这里我们创建了一 个函数,然后调用它,读取它的 doc 属性,并且确定函数对象本 身是 function 类的实例。 示例 5-1 创建并测试一个函数,然后读取它的…...
运筹学之模拟退火
目录 一、历史二、精髓思想三、案例与代码实现 一、历史 问:谁在什么时候提出模拟退火?答:模拟退火算法(Simulated Annealing,SA)是由斯图尔特柯尔斯基(Scott Kirkpatrick) 等人在 …...
PHP实现简单的爬虫功能
<?php// 目标URL $url https://example.com;// 初始化cURL $ch curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_USERAGENT, Mozilla/5…...
树莓派5-开发应用笔记
0.树莓派系统目录 /home:用户目录。 除了root用户外,其他所有的使用者的数据都存放在这个目录下,在树莓派的系统中,/home目录中有一个pi的子目录,这个就是pi用户的默认目录。 /bin: 主要放置系统的必备执行文件目录。 …...
PostgreSQL 通过 copy 命令导入几何数据 及 通过 CopyManager.copyIn() 导入几何数据
COPY命令介绍 copy是postgresql提供的一个专门用于快速导入导出数据的命令,通常用于从文件(TXT、CSV等)或标准输入输出中读取或写入数据。适合批量导入导出数据,速度快。 默认情况下,如果在处理过程中遇到错误,COPY将失败。 COPY只能用于表,不能用于视图!!! COPY…...
8.5/Q1,Charls最新文章解读
文章题目:Atherogenic index of plasma, high sensitivity C-reactive protein and incident diabetes among middle-aged and elderly adults in China: a national cohort study DOI:10.1186/s12933-025-02653-4 中文标题:中国中老年人群血…...
k8s 调整Node节点 Max_Pods
默认情况下,Kubernetes集群中一个Node最多能起110个Pod。 这是基于性能和资源管理的考虑,以确保Kubernetes集群的稳定性和可靠性。 查看kht125节点上支持的最大pod数量: kubectl describe node kht125 | grep -i “Capacity|Allocatable” -A 6 调整…...
深度补全网络:CSPN++ 有哪些开源项目
关于 CSPN(Convolutional Spatial Propagation Network) 的开源项目,目前官方或社区维护的完整实现较为有限,但以下资源可作为研究深度补全任务的参考: 1. 官方实现 & 相关论文 原始论文与代码 CSPN 的…...
使用Service发布前后端应用程序
使用Service发布前后端应用程序 文章目录 使用Service发布前后端应用程序[toc]一、创建并发布后端应用程序二、创建并发布前端应用程序三、通过前端发送流量进行测试 部署前端(Frontend)微服务和后端(Backend)微服务是比较常见的应…...
Ubuntu20.04下Docker方案实现多平台SDK编译
0 前言 熟悉嵌入式平台Linux SDK编译流程的小伙伴都知道,假如平台a要求必须在Ubuntu18.04下编译,平台b要求要Ubuntu22.04的环境,那我只有Ubuntu20.04,或者说我的电脑硬件配置最高只能支持Ubuntu20.04怎么办?强行在Ubuntu20.04下编译,编又编不过,换到旧版本我又不愿意,…...
