DispatcherServlet ——Spring MVC 的大心臟

zhoupq發表於2017-04-10

DispatcherServlet ——Spring MVC 的大心臟

  Spring MVC 的工作流程講解遍地開花,本篇主要記錄 DispatherServlet 的處理機制。這是深入瞭解 Spring MVC 框架工作機理的關鍵。

Spring MVC 的心臟

  DispatcherServlet 是 Spring MVC 的心臟,它負責接收 HTTP 請求並協調 Spring MVC 的各個元件完成請求處理的工作。DispatcherServlet 本質也是一個 Servlet,使用者必須在 web.xml 中配置。

DispatcherServlet 處理機制

  DispatcherServlet 細分之後,可以整理出三個功能:

  • 截獲 HTTP 請求,並交由 Spring MVC 框架處理
  • 處理控制層、業務層、持久層之間的呼叫關係
  • 初始化並裝配 Spring MVC 的各個元件

配置 DispatcherServlet

  在 web.xml 中配置一個 Servlet,並通過 < servlet-mapping> 指定其處理的 URL。

<!-- 1. 載入所有的配置檔案 -->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/config/**/applicationContext-*.xml</param-value>
</context-param>

<!-- 1. 配置Spring監聽 -->
<listener>
    <listener-class>
        org.springframework.web.context.ContextLoaderListener
    </listener-class>
</listener>

<!-- 2. 宣告 DispatcherServlet-->
<servlet>
    <servlet-name>springMVC</servlet-name>
    <servlet-class>
        org.springframework.web.servlet.DispatcherServlet
    </servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

<!-- 3. DispatcherServlet 匹配的 URL 模式-->
<servlet-mapping>
    <servlet-name>springMVC</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>複製程式碼

  在 1. 處,通過 contextConfigLocation 引數制定業務層和持久層的 Spring 容器的配置檔案(此處載入了全部配置檔案),可以載入單個,也可以載入多個(以“,”分隔)。ContextLoaderListener 是一個 ServletContextListener,它通過 contextConfigLoaction 引數所指定的 Spring 配置檔案啟動業務層和持久層的 Spring 容器。

  在 2. 處配置了名為 springMVC 的 DispatcherServlet,它預設自動載入 /WEB-INF/springMVC-servlet.xml(即 < servlet-name>-servlet.xml) 的 Spring 配置檔案,啟動“控制層”的 Spring 容器。

  當然,也可以顯示的指定 Spring 配置檔案,如此一來,配置檔案的名稱可不必遵守 < servlet-name>-servlet.xml 的命名規則:

<!-- 2. 宣告 DispatcherServlet-->
<servlet>
    <servlet-name>springMVC</servlet-name>
    <servlet-class>
        org.springframework.web.servlet.DispatcherServlet
    </servlet-class>

    <!-- 顯示指定 “控制層” 的 Spring 配置檔案-->
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/config/springmvc/applicationContext-mvc.xml
        </param-value>
    </init-param>

    <load-on-startup>1</load-on-startup>
</servlet>複製程式碼

  一個 web.xml 可以配置多個 DispatcherServlet,要保證 < servlet-name> 不同,通過其 < servlet-mapping> 的配置,讓每個 DispatcherServlet 處理不同的請求。

截獲 HTTP 請求

  在 3. 處通過 < servlet-mapping> 指定 DispatcherServlet 處理所有的 HTTP 請求,這裡的“/”表示瀏覽器中的 URL 都會被 DispatcherServlet 接收。大多數情況,我們都會讓 DispatcherServlet 處理以某種特定的 URL,比如以 .html 為字尾的 HTTP 請求:

<servlet-mapping>
    <servlet-name>springMVC</servlet-name>
    <url-pattern>*.html</url-pattern>
</servlet-mapping>複製程式碼

層級之間的呼叫

  多個 Spring 容器之間可以設定(通過 IOC)為父子級的關係,實現良好的解耦。在這裡,“控制層” Spring 容器將作為“業務層” Spring 容器的子容器,即“控制層”容器可以引用“業務層”容器的 Bean,而“業務層”容器的卻訪問不到“控制層”容器的 Bean,“持久層”亦同。

DispatcherServlet ——Spring MVC 的大心臟

裝配 Spring MVC 元件

  在此之前,關於 DispatcherServlet 的瞭解都只停留在配置檔案的層面,配置檔案中只涉及了載入Spring配置檔案,以及截獲 HTTP 請求。如果要想知道其他關於 DispatcherServlet 的知識——初始化並裝配 Spring MVC 元件,就必須檢視原始碼了。正所謂:不入虎穴,焉得虎子?

  IDE 提前安裝【原始碼檢視】外掛,以下是 org.springframework.web.servlet.DispatcherServletinitStrategies() 的程式碼(註釋是我新增的):

    protected void initStrategies(ApplicationContext context)
    {
        initMultipartResolver(context); // 初始化上傳檔案解析器
        initLocaleResolver(context); // 初始化本地化解析器
        initThemeResolver(context); // 初始化主題解析器
        initHandlerMappings(context); // 初始化處理器對映器
        initHandlerAdapters(context); // 初始化處理器介面卡
        initHandlerExceptionResolvers(context); // 初始化處理器異常解析器
        initRequestToViewNameTranslator(context); // 初始化請求到檢視名翻譯器
        initViewResolvers(context); // 初始化檢視解析器
        initFlashMapManager(context); // 初始化 Flash 管理器
    }複製程式碼

  一開始其實我不知道該定位到哪個方法或者說哪部分程式碼,但是我看到關鍵字 init,以及引數 ApplicationContext context 大概知道這應該是初始化方法了,而且該方法內呼叫了其他以 init 開頭的方法,而且方法名都是平常熟悉的詞彙,這些就是元件了,八九不離十。最後我通過直譯和找資料給上述程式碼新增了註釋。

  initStrategies() 方法將在 WebApplicationContext 初始化後自動執行,此時 Spring 上下文中的 Bean 已經初始化完畢。該方法的工作是通過反射機制查詢並裝配 Spring 容器中使用者顯示自定義的元件 Bean,如果找不到再裝配預設的元件例項。

小結

  要想深入 Spring MVC 框架的工作機理,必須先了解以下三個問題:

  • DispatcherServlet 如何截獲 HTTP 請求
  • 控制層、業務層和持久層之間的呼叫關係
  • 如何初始化 Spring MVC 的各個元件,並將其裝配到 DispatcherServlet 中

我的部落格地址:DispatcherServlet——Spring MVC 的大心臟

相關文章