圖解 Spring:HTTP 請求的處理流程與機制【3】

3. HTTP 請求在 Web 應用中的處理流程

在穿越了 Web 容器以後,HTTP 請求將被投送到 Web 應用,咱們繼續以 Tomcat 爲例剖析後續流程。Web 容器與 Web 應用的銜接是經過配置文件 web.xml 完成的。web.xml 是遵循 Java Servlet 標準規範的配置文件,咱們經過這份配置文件定義構成 Web 應用的各類核心組件和初始化配置,其中包括:過濾器 Filter、監聽器 Listener、伺服器 Servlet 等等。不一樣組件分別承擔不一樣的功能,在介紹 Web 應用處理 HTTP 請求流程以前,咱們照例先來了解一下這些核心組件。html

3.1 Web 應用核心組件簡介

3.1.1 過濾器 Filter

過濾器 Filter 負責對 HTTP 請求作預處理,接着將請求交給 Servlet 進行處理並生成響應,最後 Filter 再對響應進行後處理。從 HTTP 請求的處理過程來看,Filter 主要參與如下幾個環節:java

  • 在 HttpServletRequest 到達 Servlet 以前,攔截客戶的 HttpServletRequest。
  • 根據須要檢查 HttpServletRequest,也能夠修改 HttpServletRequest 報文頭和數據。
  • 在 Servlet 生成的 HttpServletResponse 抵達客戶端以前,攔截 HttpServletResponse。
  • 根據須要檢查 HttpServletResponse,也能夠修改 HttpServletResponse 報文頭和數據。

過濾器映射 filter-mapping,用於聲明 Web 應用將會用到的過濾器,過濾器可被映射到一個 Servlet 或 URL 模式。若是將過濾器映射到一個 Servlet 上,那它就做用於特定 Servlet。若是將過濾器映射到一個 URL 模式,那麼它將做用於任何資源,只要該資源的 URL 與 URL 模式匹配。若是對某個資源的請求匹配到多個 Filter,那麼在處理 HTTP 請求過程當中,Tomcat 將按照過濾器映射 filter-mapping 在配置文件 web.xml 中的前後順序來執行,在前面的先執行,在後面的後執行,多個過濾器 Filter 能夠組成調用鏈。web

URL 模式匹配有三種類型規則:spring

  • 精確匹配:如「/foo.htm」,那隻會匹配「foo.htm」這個 URL。
  • 路徑匹配:如「/foo/*」,那隻會匹配以 foo 爲前綴的 URL。
  • 後綴匹配:如「*.htm」,那隻會匹配全部以「.htm」爲後綴的 URL。
 1 <filter>
 2     <filter-name>encodingFilter</filter-name>
 3     <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
 4     <init-param>
 5         <param-name>encoding</param-name>
 6         <param-value>UTF-8</param-value>
 7     </init-param>
 8     <init-param>
 9         <param-name>forceEncoding</param-name>
10         <param-value>true</param-value>
11     </init-param>
12 </filter>
13 <filter-mapping>
14     <filter-name>encodingFilter</filter-name>
15     <url-pattern>/*</url-pattern>  
16 </filter-mapping>
3.1.2 監聽器 Listener

監聽器 Listener 主要用於監聽 Application、Session、Request 等對象的變化,每當這些對象發生變化就會回調用對應的監聽方法。例如:在 Servlet API 中有一個 ServletContextListener 接口,它可以監聽 ServletContext 對象的生命週期,實際上就是監聽 Web 應用的生命週期。當 Servlet 容器啓動或終止Web 應用時,會觸發 ServletContextEvent 事件,該事件由 ServletContextListener 來處理。編程

1 <listener>
2     <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
3 </listener>
3.1.3 伺服器 Servlet

伺服器 Servlet 負責處理客戶端訪問動態資源的 HTTP 請求,接口 javax.servlet.Servlet 定義了全部 Servlet 必需要實現的方法。api

方法名稱 功能說明
destroy() 由 Servlet 容器調用,用於關閉中止 Servlet 提供的服務
getServletConfig() 獲取 Servlet 初始化和啓動時參數的配置信息對象 ServletConfig
getServletInfo() 獲取 Servlet 的說明信息,包括:做者、版本和版權等等
init() 由 Servlet 容器調用,籍由配置 ServletConfig 完成 Servlet 初始化,啓動對外服務
service() 由 Servlet 容器調用,讓 Servlet 處理某個 HTTP 請求

從 HTTP 請求的處理過程來看,伺服器 Servlet 主要參與如下幾個環節:微信

  • 接收請求:客戶端請求會被封裝成 HttpServletRequest 對象,包含報文頭參數和報文體等信息。
  • 處理請求:一般調用 Servlet 的方法 service、doPost 或 doGet 等方法處理請求,並進一步調用業務層相應邏輯對其進行處理等。
  • 反饋響應:處理完請求後,能夠轉發(forward)、重定向(redirect)到某個視圖頁面或者直接返回結果數據,轉發是 HttpServletRequest 的方法,重定向是 HttpServletResponse 的方法。

在老兵哥的讀書年代,Web 應用相對簡單,主要是各類信息管理系統。當時 Spring 還沒有誕生,主流技術棧是 JSP/Servlet,老兵哥我開發 Web 應用時主要編寫繼承自 HttpServlet 子類,將各類業務邏輯功能分別交由不一樣的 HttpServlet 子類實現。HttpServlet 繼承自 GenericServlet,後者實現了接口 Servlet。架構

隨着數字化和互聯網化的不斷推動,業務系統變得愈來愈複雜,HttpServlet 子類越寫越多,web.xml 配置文件愈來愈複雜,這致使系統的擴展維護愈來愈困難,手工做坊式的開發方法已經跟不上業務發展的步伐了。mvc

時勢造英雄,Spring 就是在這種背景下呼之而出的,它創造性地發明了控制反轉 IOC 和麪向切面編程 AOP,極大地下降複雜度。以下面配置示例所示,整個 Web 應用只須要配置一個 Servlet 就能夠了,它就是 Spring 的前置分發器 DispatcherServlet:app

 1 <servlet>
 2     <servlet-name>mvc</servlet-name>
 3     <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
 4     <init-param>
 5         <param-name>contextConfigLocation</param-name>
 6         <param-value>classpath:mvc-servlet.xml</param-value>
 7     </init-param>
 8     <load-on-startup>1</load-on-startup>
 9 </servlet>
10 <servlet-mapping>
11     <servlet-name>mvc</servlet-name>
12     <url-pattern>/api</url-pattern>
13 </servlet-mapping>

3.2 Web 應用處理 HTTP 請求的流程

以下圖所示,Web 應用處理 HTTP 請求的流程主要是穿越監聽器 Listener 和過濾器鏈 Filters,最終抵達伺服器 Servlet 的過程:

Web應用核心組件

原先咱們將 Web 應用的複雜度直接暴露給了 Tomcat,如今 Spring 經過控制反轉 IOC 和麪向切面編程 AOP 等新技術接管了 Web 應用的複雜度。如前面 Servlet 的配置示例,整個 Web 應用只須要配置 Spring 提供的前置分發器 DispatcherServlet,開發者無需再編寫和配置 HttpServlet 子類,全部業務邏輯功能將按照 Spring 的標準規範來開發,從原先的編寫 Listener\Filter\Servlet 改成編寫 Spring Component,包括:Controller、Service、Repository 等。

Tomcat 在接收到某個 Web 應用的 Http 請求以後,它會將全部請求都轉交給前置分發器 DispatcherServlet,再由 DispatcherServlet 將請求派發給具體業務邏輯進行處理。DispatcherServlet 就是從 HttpServlet 派生的子類,它的類圖關係以下所示:

DispatcherServlet類圖

3.3 Web 應用架構演進過程解析

Web 應用架構的演進過程就像創業孵化過程,最初創業團隊打造的產品很簡單,你們採用手工做坊模式來快速打造最小化可行產品,這時候團隊的組織架構也跟產品同樣扁平簡單。但隨着產品被愈來愈多的用戶使用,功能變得愈來愈複雜,接着必須引進新架構纔能有效管理複雜度,同時團隊規模的擴展也須要與業務發展匹配的組織架構,這樣才能保證產品的不斷髮展。

Web 應用架構的演化過程跟 Tomcat 體系結構的造成過程相似,老兵哥會常常藉助「俄羅斯套娃」這個模型來闡述架構,Web 容器和 Web 應用這兩層的架構原則是相似的,就像大娃娃套着小娃娃同樣。

Spring 的 IOC 容器跟 Tomcat 的 Servlet 容器相似,也是經過配置文件等方式來定義組件,而後在啓動過程當中將這些定義好的組件初始化並添加到容器當中,後續使用時從容器查找獲取。早期 Web 應用主要由大量開發者編寫的 Filter\Listener\Servlet 等組件構成,這些核心組件的配置所有都經過 web.xml 配置文件來維護,那麼 Web 應用和 Web 容器之間其實不是鬆耦合的,而引進 Spring 以後就變成只須要配置 DispatcherServlet 等少許組件了,達到了分層架構的要求,更加有利於開發複雜的 Web 應用。若是採用架構的專業術語來描述,這就是經典的分層架構模式,層與層之間鬆耦合,僅經過少許的接口銜接,每層內部高內聚。

 

本文主要價值是幫助你們梳理出端到端的全流程框架,也就是咱們常說的全局視角或者上帝視角。有了這個框架以後,咱們能夠根據本身的須要按圖索驥找相關節點的資料來研究學習,不至於陷入細節找不到方向。固然,考慮到咱們每一個人的工做學習狀況不一樣,平時遇到的問題也不一樣,本文內容沒法覆蓋全部人遇到的問題,歡迎你們留言提問,也歡迎關注個人微信公衆號「IT老兵哥」交流互動,我會盡力盡快解答你們提出的問題,謝謝!

本系列其餘文章索引以下:

相關文章
相關標籤/搜索