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.pswp.cn/news/902136.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/902136.shtml
英文地址,請注明出處:http://en.pswp.cn/news/902136.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

《Learning Langchain》閱讀筆記3-基于 Gemini 的 Langchain如何從LLMs中獲取特定格式

純文本輸出是有用的&#xff0c;但在某些情況下&#xff0c;我們需要 LLM 生成結構化輸出&#xff0c;即以機器可讀格式&#xff08;如 JSON、XML 或 CSV&#xff09;或甚至以編程語言&#xff08;如 Python 或 JavaScript&#xff09;生成的輸出。當我們打算將該輸出傳遞給其他…

中間件--ClickHouse-12--案例-1-日志分析和監控

1、案例背景 一家互聯網公司需要實時分析其服務器日志、應用日志和用戶行為日志&#xff0c;以快速發現潛在問題并優化系統性能。 2、需求分析 目標&#xff1a;實時分析日志數據&#xff0c;快速發現問題并優化系統性能。數據來源&#xff1a; 服務器日志&#xff1a;如 Ng…

多道程序和多任務操作系統區別

多道程序 vs. 多道任務&#xff1a;對比分析 ? 共同點 方面共同特征核心機制都依賴于進程/任務切換執行需求實現多個程序或任務"并發"執行系統支持都需要操作系統的支持&#xff08;如調度算法、內存管理&#xff09;本質目標提高資源利用率&#xff08;CPU不空轉…

齊次坐標變換+Unity矩陣變換

矩陣變換 變換&#xff08;transform)&#xff1a;指的是我們把一些數據&#xff0c;如點&#xff0c;方向向量甚至是顏色&#xff0c;通過某種方式&#xff08;矩陣運算&#xff09;&#xff0c;進行轉換的過程。 變換類型 線性變換&#xff1a;保留矢量加和標量乘的計算 f(x)…

閑來無事,用HTML+CSS+JS打造一個84鍵機械鍵盤模擬器

今天閑來無聊&#xff0c;突發奇想要用前端技術模擬一個機械鍵盤。說干就干&#xff0c;花了點時間搞出來了這么一個有模有樣的84鍵機械鍵盤模擬器。來看看效果吧&#xff01; 升級版的模擬器 屏幕錄制 2025-04-18 155308 是不是挺像那么回事的&#xff1f;哈哈&#xff01; 它…

智慧城市:如同為城市裝上智能大腦,開啟智慧生活

智慧城市的概念隨著信息技術的飛速發展而逐漸興起&#xff0c;它通過集成物聯網、大數據、人工智能和數字孿生等先進技術&#xff0c;為城市管理和居民生活帶來了前所未有的智能化變革。本文將深入探討這些核心技術及其在智慧城市的典型應用場景&#xff0c;展示智慧城市如何提…

科技快訊 | 智譜開源最新GLM模型系列;“AI 洗頭店”現身廣州;ChatGPT上線圖庫功能

智譜開源最新GLM模型系列&#xff0c;啟用全球域名“Z.ai” 4月15日&#xff0c;智譜開源最新GLM模型系列&#xff0c;包括32B和9B尺寸&#xff0c;涵蓋基座、推理、沉思三類模型&#xff0c;全部遵循MIT開源許可協議。推理模型GLM-Z1-32B-0414實測推理速度達200 tokens/秒&…

第32講:衛星遙感與深度學習融合 —— 讓地球“讀懂”算法的語言

目錄 ?? 一、講講“遙感+深度學習”到底是干啥的? ? 能解決什么問題? ?? 二、基礎原理串講:深度學習如何“看懂”遙感圖? ?? 遙感圖像數據類型: ?? CNN的基本思路: ?? 三、實戰案例:用CNN對遙感圖像做地類分類 ?? 所需R包: ??? 步驟一:構建訓…

【多線程5】面試常考鎖知識點

文章目錄 悲觀/樂觀鎖掛起等待鎖/自旋鎖偏向鎖輕量級/重量級鎖鎖升級CASCAS引發的ABA問題解決方案 原子類 公平/不公平鎖可重入鎖ReentrantLock讀寫鎖 Callable接口 這里的“悲觀”“樂觀”“掛起等待”“自旋”“輕量級”“重量級”“公平”“非公平”“可重入”僅代表某個鎖的…

第三屆世界科學智能大賽新能源賽道:新能源發電功率預測-數據處理心得體會1

看懂數據 比賽數據說明&#xff1a; 文檔&#xff08;報名之后可以下載&#xff09;大小操作初賽測試集.zip94MB下載初賽訓練集.zip632MB下載output.zip145KB下載 任務和主題 AI新能源功率預報&#xff1a;根據歷史發電功率數據和對應時段多類別氣象預測數據&#xff0c;實…

【云馨AI-大模型】2025年4月第三周AI領域全景觀察:硬件革命、生態博弈與國產化突圍

一、硬件算力突破點燃多智能體時代 谷歌在4月12日Cloud Next大會發布第七代TPU Ironwood&#xff0c;單芯片算力達4614 TFLOPs&#xff0c;較前代內存提升6倍&#xff0c;專為AI推理場景優化。配合發布的Gemini 2.5 Flash模型通過"思考"功能實現成本優化&#xff0c…

第3章 垃圾收集器與內存分配策略《深入理解Java虛擬機:JVM高級特性與最佳實踐(第3版)》

第3章 垃圾收集器與內存分配策略 3.2 對象已死 Java世界中的所有對象實例&#xff0c;垃圾收集器進行回收前就是確定對象哪些是活著的&#xff0c;哪些已經死去。 3.2.1 引用計數算法 常見的回答是&#xff1a;給對象中添加一個引用計數器&#xff0c;有地方引用&#xff0…

超詳細VMware虛擬機擴容磁盤容量-無坑版

1.環境&#xff1a; 虛擬機&#xff1a;VMware Workstation 17 Pro-17.5.2 Linux系統&#xff1a;Ubuntu 22.04 LTS 2.硬盤容量 虛擬機當前硬盤容量180G -> 擴展至 300G 3.操作步驟 &#xff08;1&#xff09;在虛擬機關機的狀態下&#xff0c;虛擬機硬盤擴容之前必…

HarmonyOS:1.4 - HarmonyOS應用程序框架基礎

判斷題 1.在基于Stage模型開發的應用項目中都存在一個app.json5配置文件、以及一個或多個module.json5配置文件。 正確(True) 2.一個應用只可以包含一個UIAbility組件。 錯誤(False) 3.Background狀態在UIAbility實例銷毀時觸發。可以在onDestroy()回調中進行系統資源的釋…

HTTP HTTPS RSA

推薦閱讀 小林coding HTTP篇 文章目錄 HTTP 80HTTP 響應碼1xx&#xff1a;信息性狀態碼&#xff08;Informational&#xff09;2xx&#xff1a;成功狀態碼&#xff08;Success&#xff09;3xx&#xff1a;重定向狀態碼&#xff08;Redirection&#xff09;4xx&#xff1a;客戶端…

ORACLE數據庫轉國產阿里OceanBase數據庫

1.BLOB類型修改 將接口內oracle.sql.BLOB改為java.sql.Blob 2.REGEXP_LIKE 判斷函數正則表達式中字符轉義問題 OB的正則表達式使用的是標準的Linux模式,oracle是黑盒子,在處理部分轉義符([])的時候, Oracle無需使用轉義符,OB務必使用轉義符,加/轉義處理,例如在regexp_like(t…

STM32的三種啟動方式

目錄 一、從主閃存存儲器啟動&#xff08;Main Flash Memory&#xff09; 二、從系統存儲器啟動&#xff08;System Memory&#xff09; 三、從內置SRAM啟動&#xff08;Embedded SRAM&#xff09; 一、從主閃存存儲器啟動&#xff08;Main Flash Memory&#xff09; >&g…

Flutter使用flutter_driver進行自動化測試

Flutter自動化測試實踐指南 作為一名iOS開發者&#xff0c;我最近對Flutter的自動化測試產生了濃厚興趣。在開發過程中&#xff0c;我發現自動化測試對于保證應用質量至關重要&#xff0c;特別是像我們這樣的創業團隊&#xff0c;測試資源有限的情況下。 搭建Flutter自動化測…

Halcon應用:九點標定-手眼標定

提示&#xff1a;若沒有查找的算子&#xff0c;可以評論區留言&#xff0c;會盡快更新 Halcon應用&#xff1a;九點標定-手眼標定 前言一、Halcon應用&#xff1f;二、應用實戰1、圖形理解[eye-to-hand]&#xff1a;1.1、開始應用2、 圖形理解[eye-in-hand]2.1、 開始應用 前言…

【C++11】列表初始化、右值引用、完美轉發、lambda表達式

&#x1f4da; 博主的專欄 &#x1f427; Linux | &#x1f5a5;? C | &#x1f4ca; 數據結構 | &#x1f4a1;C 算法 | &#x1f310; C 語言 上篇文章&#xff1a;unordered_map、unordered_set底層編寫 下篇文章&#xff1a;C11&#xff1a;新的類功能、模板的可…