前言
下面是一個SpringMVC應用的配置文件,需要注意兩個地方,一個是ContextLoaderListener,一個是dispatcherServlet。web容器正是通過這兩個配置才和spring管理起來。ContextLoaderListener與web容器的ServletContext關聯,為Spring的IOC容器提供了一個宿主環境。在建立起IOC容器體系之后,把DispatcherServlet作為SpringMVC處理web請求的轉發器建立起來,完成響應http請求的准備。
SpringMVC啟動過程大致分為兩個階段:
第一階段.ContextLoaderListener初始化,實例化IOC容器,並將此容器注冊到ServletContext中。
第二階段DispatcherServlet初始化,建立自己的上下文,也注冊到ServletContext中。
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <display-name>ota</display-name> <description>ota web application</description> <!-- 系統組件加載順序:context-param -> listener -> filter -> servlet --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:/applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- GZip --> <filter> <filter-name>gzipFilter</filter-name> <filter-class>com.travelsky.ibeplus.compress.Compress2WayFilter</filter-class> </filter> <filter-mapping> <filter-name>gzipFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- Servlet that dispatches request to registered handlers (Controller implementations). --> <servlet> <servlet-name>ota</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/mvc-core-config.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <!-- 需要定義在對應的servlet之后 --> <servlet-mapping> <servlet-name>ota</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
1、Spring IOC容器的啟動
Spring IOC容器的啟動在我博客中這一篇博文“Spring是怎樣啟動的” 有詳細介紹,這里再簡單概述下。
ContextLoaderListener實現ServletContextListener,這個接口里面的方法會結合web容器的生命周期被調用。因為ServletContextListener是ServletContext的監聽者,如果ServletContext發生變化,會觸發相應的事件,而監聽者一直對這些事件進行監聽,如果接受到了監聽的事件,就會作於相應處理。例如在服務器啟動,ServletContext被創建的時候,ServletContextListener的contextInitialized()方法被調用,從而拉開了初始化Spring IOC容器的序幕
2、DispatcherServlet的啟動 (HttpServletBean * FrameworkServlet * DispatcherServlet)
DispatchServlet本質上是一個Servlet,web容器啟動的時候,servlet也會初始化,其init方法被調用,開啟初始化之旅。
DispatchServlet會建立自己的上下文來持有Spring MVC特殊的bean對象,在建立這個自己持有的Ioc容器的時候,會從ServletContext中得到根上下文作為DispatchServlet上下文的parent上下文。有了這個根上下文,再對自己持有的上下文進行初始化,最后把自己持有的這個上下文保存到ServletContext中,供以后檢索和使用。
下面來詳細解釋下:

- DispatchServlet名如其義,它的本質上是一個Servlet,子類不斷的對HttpServlet父類進行方法擴展
- HttpServlet有兩大核心方法:init()和service()方法。HttpServletBean重寫了
init()方法,在這部分,我們可以看到其實現思路:公共的部分統一來實現,變化的部分統一來抽象,交給其子類來實現,故用了abstract class來修飾類名。此外,HttpServletBean提供了一個HttpServlet的抽象實現,使的Servlet不再關心init-param部分的賦值,讓servlet更關注於自身Bean初始化的實現 - FrameworkServlet提供了整合web javabean和spring application context的整合方案。在源碼中可以看到通過執行
initWebApplicationContext()方法和initFrameworkServlet()方法實現 - DispatchServlet是HTTP請求的中央調度處理器,它將web請求轉發給controller層處理,它提供了敏捷的映射和異常處理機制,DispatchServlet轉發請求的核心代碼在doService()方法中實現
2.1 HttpServletBean


上圖是抽象類HttpServletBean的實現,我們知道HttpServlet有兩大核心方法:init()和service()方法。HttpServletBean重寫了init()方法,在這部分,我們可以看到其實現思路:公共的部分統一來實現,變化的部分統一來抽象,交給其子類來實現,故用了abstract class來修飾類名。此外,HttpServletBean提供了一個HttpServlet的抽象實現,使的Servlet不再關心init-param部分的賦值,讓servlet更關注於自身Bean初始化的實現。
2.2 FrameworkServlet
在HttpServletBean中有一方法initServletBean()需要子類去實現,因此 FrameworkServlet需要實現initServletBean()方法



2.3 DispatcherServlet
在FrameworkServlet中有一方法onRefresh()需要子類去實現,因此DispatcherServlet需要去實現onRefresh()方法。


另外一些MVC的特性初始化時在initStrategies()中實現的,包括支持國際化的LocalResolver、支持Request映射的HandlerMappings,以及視圖生成的ViewResolver等等。
比如初始化HandlerMappings,在SpringMVC中,HandlerMappings的作用是為Http請求找到對應的Controller控制器,來進一步處理請求。
注意:
在web.xml配置文件中,DispatcherServlet中有一配置<load-on-startup>1</load-on-startup>,表示在容器啟動的時候就加載該servlet。
當<load-on-startup>值為0或者大於0時,表示容器在應用啟動時就加載這個servlet(實例化並調用其init()方法)。
當是一個負數時或者沒有指定時,則表示第一次請求該servlet才加載。
正數的值越小,啟動該servlet的優先級越高。
3. DispatcherServlet類和ContextLoaderListener類的關系圖:

小結:首先,用ContextLoaderListener初始化上下文,接着使用DispatchServlet來初始化WebMVC的上下文。
