当前位置: 首页 > news >正文

chapter32_SpringMVC与DispatcherServlet

一、简介

从本章节开始进入SpringMVC的学习,SpringMVC最重要的类就是DispatcherServlet

DispatcherServlet的本质是一个Servlet,回顾一下Servlet

  • JavaWeb就是基于Servlet的
  • Servlet接口有5个方法
  • Servlet实现类是HttpServlet,自定义的Servlet需要继承HttpServlet,重写service方法
  • 使用SpringMVC后,DispatcherServlet就是唯一的Servlet,所有的请求由他分发

二、目标

  1. 手写SpringMVC的核心类DispatcherServlet
  2. 通过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方法调用

http://www.mrgr.cn/news/98624.html

相关文章:

  • 来个去照片背景的GUI程序
  • 毛笔书体检测-hog+svm python opencv源码
  • Linux上位机开发实践(SoC和MCU的差异)
  • Information-Theoretic Limits of Bistatic Integrated Sensing and Communication
  • BTS7960 直流电机控制程序
  • SAP ECCS 标准报表 切换为EXCEL电子表格模式
  • 构建大模型知识库(一)
  • 【c++深入系列】:new和delete运算符详解
  • 安卓手游逆向
  • mysql表类型查询
  • 通过建模和仿真进行高速连接器设计
  • Python爬虫第15节-2025今日头条街拍美图抓取实战
  • GIS开发笔记(7)结合osg及osgEarth实现不同高度下的三个圆形区域形成的三维覆盖轮廓区域绘制
  • **Microsoft Certified Professional(MCP)** 认证考试
  • argparse
  • 子函数嵌套的意义——以“颜色排序”为例(Python)
  • Python抽象基类
  • Function Calling是什么?
  • CS5346 - CHARTS: Chart with Point / Bar / Line / Box
  • 神经光子渲染:物理级真实感图像生成——从麦克斯韦方程到深度学习