SpringMVC DispatcherServlet 啟動和載入過程(源碼調試)

来源:https://www.cnblogs.com/cyhbyw/archive/2018/03/31/8683251.html
-Advertisement-
Play Games

在閱讀本文前,最好先閱讀以下內容(當然,如果對 Servlet 已經有所瞭解,則可跳過): http://www.cnblogs.com/cyhbyw/p/8682078.html http://www.cnblogs.com/cyhbyw/p/8682307.html http://www.cnb ...


在閱讀本文前,最好先閱讀以下內容(當然,如果對 Servlet 已經有所瞭解,則可跳過):

http://www.cnblogs.com/cyhbyw/p/8682078.html

http://www.cnblogs.com/cyhbyw/p/8682307.html

http://www.cnblogs.com/cyhbyw/p/8682632.html

============分隔線==========================

 

在使用 SpringMVC 進行 Web 開發時,通常在 web.xml 中配置的 Servlet  都是 org.springframework.web.servlet.DispatcherServlet,那這個 DispatcherServlet 又是如何被 Tomcat 容器(或者其它容器)啟動並載入進來的呢?

帶著這個問題,寫了個簡單Demo進行源碼調試。

Demo代碼地址:https://github.com/cyhbyw/springMVC_atguigu_TongGang

Demo代碼工程:springMVC_DebugSourceCode

 

首先從靜態代碼的角度,可以看到 DispatcherServlet 類的承繼結構如下圖所示

  • HttpServlet 及以上部分是 Servlet 標準中提供的介面及類
  • DispatcherServlet、FrameworkServlet、HttpServletBean 三者是 SpringMVC 提供的類,且後者依次分別是前者的父類

 

現在開始源碼調試:

首先調用了 DispatcherServlet 的構造函數,並且從堆棧信息中可以看出,這是由 Tomcat 調用的

接下來當然是調用父類 FrameworkServlet 的構造函數

 

構造函數完成後,調用 Servlet 生命周期的 init() 方法;

提示,此處是 HttpServletBean 中的 init() 方法重寫了GenericServlet中的 init() 方法;

這就是之前說的,建議重寫這個空的 init() 方法而不建議重寫那個 init(ServletConfig config) 方法,看來 SpringMVC 也確實是這樣做的;

接下來代碼走到 Line136行,初始化容器Bean

 接下來代碼走到 Line493 行,初始化Web應用上下文

 

 接下來代碼走到 Line552 行,創建Web應用上下文

 

獲取到需要創建的Bean的Class

直接調用 getContextClass() 方法

而它內置的 contextClass 其實就是 XmlWebApplicationContext

 XmlWebApplicationContext 的繼承結構如下圖所示,不用說,肯定也是 ApplicationContext 家庭中的成員

Line627 行就實例化了 XmlWebApplicationContext 

同時,代碼會走到 Line633 行,配置並刷新Web應用上下文

Line655 添加了一個應用監聽器;(重要,後面會取出來用到)

註意,這裡(SourceFilteringListener類中)方法入參處的 ApplicationListener delegate = FrameworkServlet$ContextRefreshListener,且 SourceFilteringListener 類成員變數中的 GenericApplicationListener delegate = GenericApplicationListenerAdapter;同時方法入參中的 delegate 會被 GenericApplicationListenerAdapter 包裝後賦值給成員變數的 delegate(有點繞,所以用了三種顏色以示區分)

可以這樣來記憶或理解:

一、對於 SourceFilteringListener 來說,其成員變數 delegate 的類型是 GenericApplicationListenerAdapter 

二、對於 GenericApplicationListenerAdapter  來說,它也有個叫做 delegate 的成員變數,且這個 delegate 的類型是 FrameworkServlet$ContextRefreshListener

(雖然這兩個同名叫做 delegate 的成員變數有點繞,但它們比較重要,後面會用到)

SourceFilteringListener 構造完成後,回到上一層方法調用處;

接下來,代碼走到 Line667 行進行刷新

 

這個 refresh() 方法是 Spring 中非常重要的一個方法,會調用多個方法執行多個動作,包括初始化BeanFactory、容器後處理器處理、初始化MessageSource、註冊監聽器等動作;

refresh() 方法非常重要!!!

refresh() 方法非常重要!!!

refresh() 方法非常重要!!!

這裡,暫時關心的是,它會讀取我們為 SpringMVC 所編寫的配置文件中的內容(如 annotation-driven & default-servlet-handler 等,這屬於上一篇文章的內容,具體可參見 這裡);

之後,它會調用 Line541 行的方法,完成刷新

經過幾個方法的調用,代碼走到 Line136 ,並且此處的 listener=SourceFilteringListener(通過 Line125 獲取到之前添加進來的Listener,且這個 listener=SourceFilteringListener)

 然後調用 SourceFilteringListener 的 onApplicationEvent() 方法

 

繼續調用

繼續調用,註意當前類是 SourceFilteringListener,且這個 delegate=GenericApplicationListenerAdapter(就是之前設置進來的

現在來到 GenericApplicationListenerAdapter 類中,註意此處的 delegate=FrameworkServlet$ContextRefreshListener(之前設置進來的),所以,實際上會調到 ContextRefreshListener 的 onApplicationEvent() 方法

進而調用到 FrameworkServlet 中內部類 ContextRefreshListener  的 onApplicationEvent() 方法,而它又是直接調用到 FrameworkServlet  的 onApplicationEvent() 方法

這個方法會調用到 onRefresh() 方法;而 FrameworkServlet 的 onRefresh() 方法預設實現為空(讓子類擴展)

自然,會調用到 DispatcherServlet 的 onRefresh() 方法上,而這個方法實際上調用了其它的一系列初始化方法,如 initHandlerMappings(context) & initHandlerAdapters(context),這樣在容器啟動的過程中,就已經初始化完成 HandlerMapping & HandlerAdapter

至此,DispatcherServlet 中與 Servlet 生命周期相關的 constructor() & init() 方法就已經基本完成了,接下來,就是對請求的響應,這會依次調用 Servlet 的 service() 方法,不屬於本文範疇啦~~~

 

簡單總結起來,Tomcat 容器啟動並載入 DispatcherServlet 時所做的主要工作如下:

  • 調用 DispatcherServlet 的構造器(當然也會調用父類的構造器,不過構造器預設實現為空;這個動作很短,基本上可以忽略)
  • 調用 GenericServlet 的 init() 方法,不過,這被 HttpServletBean 重寫了;同時,重寫的 HttpServletBean  的 init() 方法調用了 initServletBean() 方法;而 initServletBean() 方法會完成以下操作:
  1. 初始化(創建)一個 WebApplicationContext(實際上是 WebApplicationContext 類)
  2. 調用 AbstractApplicationContext 的 refresh() 方法,完成 BeanFactory創建、讀取 SpringMVC 配置文件內容、處理容器後處理器、初始化MessageResource、註冊監聽器等工作
  3. 通過上一步中讀取到的內容,初始化 HandlerMapping & HandlerAdapter 等工作
  4. ==上面3個步驟才是重要內容==

 

總的來說,DispatcherServlet 還是一個 Servlet,遵循 constructor() --> init() --> service() --> destroy() 方法的調用流程。只不過,它的這個 init() 方法確實比較複雜(這就是本文為什麼會這麼長的原因,不過,看到此處的讀者,恭喜,您已經看完啦!)。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 文本匹配 在表單輸入項裡面輸入值,根據輸入值,點擊判斷按鈕,讓對應的覆選框選中 獲取覆選框狀態 點擊按鈕,獲取覆選框狀態為選中的個數,並將結果彈出在頁面 基礎頁面效果如下: 屬性更改 當覆選框被選中時,覆選框對應的文本顏色為紅色; 當覆選框未被選中時,覆選框對應的文本顏色為黑色; 基礎頁面效果如下: ...
  • 個人想到的解決方法有兩種,一種是 .replace(' old ',' new ') 第一個參數是需要換掉的內容比如空格,第二個是替換成的內容,可以把字元串中的空格全部替換掉. 第二種方法是像這樣 先用 .split(str,num) (str -- 分隔符,預設為所有的空字元,包括空格、換行(\n ...
  • 首先定義一個compare函數: 然後調用該函數就可以對List中的元素排序: 要求ListA中的元素有value這個屬性才行,當然也可以把value換成ListA中的元素的其他共有屬性也可以。感覺和Java差不多。 ...
  • 博客代碼地址:https://gitee.com/llQ13/four_operations/tree/master/SZYSExp_1 一、題目描述: 實踐能力的提高當然就是得多動手了,那麼就從第一個個人項目開始吧,用一周的時間完成一個基於控制台的四則運算程式,實現一個自動生成小學四則運算題目的命 ...
  • 1,判斷一個字元串中的每一個字母是否都在另一個字元串中,可以利用集合的特性來解,集合的元素如果存在,再次更新(update) 是添加不進集合的,那麼集合的長度還是跟原來一樣,如果添加進去,集合長度就會增加 2,如果是多個字元呢? 3,統計出現次數最多的字元 這裡有個lambda表達式, key指定按 ...
  • Java開發,基於控制台的四則運算表達式生成和批改,能夠實現分數計算和真分數結果顯示。GitHub:https://github.com/Umbrellazc/JavaCalculation ...
  • 1.需求分析 2.功能設計 基本功能 基本功能 拓展功能 拓展功能 高級功能 高級功能 3.設計實現 4.代碼說明 遞歸查重 遞歸查重 // //代碼功能:遞歸實現二叉樹查重 // //實現思路: //1、先判斷兩個算式答案相不相同,不相同則算式一定不相同。 //2、然後對兩式計算左右兩邊的值是否能 ...
  • 再次重申學習的是某位THU大神,網址貼下 http://nbviewer.jupyter.org/github/lijin THU/notes python/tree/master/ 只貼了我不太熟悉的 適合有其他編程語言基礎的看 Chat 4 list 列表的加法,相當於將兩個列表按順序連接 我們 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...