1. Spring MVC核心類與接口
1.1 DispatcherServlet:前置控制器
Spring 提供的前置控制器,所有的請求都經(jīng)過它來統(tǒng)一分發(fā)。在 DispatcherServlet 將請求分發(fā)給 Spring Controller 之前,需要借助于 Spring 提供的 HandlerMapping 定位到具體的 Controller。
DispatcherServlet 也是間接最高繼承了 HttpServlet.
1.2 HandlerMapping 接口:處理請求的映射
HandlerMapping 接口的實(shí)現(xiàn)類:
-
SimpleUrlHandlerMapping:通過配置文件,把一個(gè) URL 映射到Controller類上; -
DefaultAnnotationHandlerMapping:通過注解,例如@RequestMapping,把一個(gè) URL 映射到Controller類上;
1.3 HandlerAdapter 接口:處理請求的映射
Spring MVC 通過 HandlerAdapter 來實(shí)際調(diào)用處理函數(shù)。
例如:
AnnotationMethodHandlerAdapter:DispatcherServlet 中根據(jù) HandlerMapping 找到對應(yīng)的 Handler Method 后,首先檢查當(dāng)前工程中注冊的所有可用的 HandlerAdapter,根據(jù) HandlerAdapter 中的 supports() 方法找到可以使用的 HandlerAdapter。
通過調(diào)用 HandlerAdapter 中的 handle() 方法來處理及準(zhǔn)備 Handler Method 中的參數(shù)及 annotation (這就是 Spring MVC 如何將 Reqeust中的參數(shù)變成 Handler Method 中的輸入?yún)?shù)的地方),最終調(diào)用實(shí)際的 Handler Method。
接口定義如下:
public interface HandlerAdapter {
boolean supports(Object var1);
@Nullable
ModelAndView handle(HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception;
long getLastModified(HttpServletRequest var1, Object var2);
}
1.4 Controller接口:控制器
由于我們使用了 @Controller 注解,添加了 @Controller 注解的類就可以擔(dān)任控制器(Action)的職責(zé),所以我們并沒有用到這個(gè)接口。
需要為并發(fā)用戶處理請求,因此實(shí)現(xiàn) Controller 接口時(shí),必須保證線程安全并可重用。
一旦 Controller 處理完用戶請求,則返回 ModelAndView 對象給 DispatcherServlet 前置控制器,ModelAndView 中包含了模型(Model)和視圖(View)。
- 從宏觀角度考慮,DispatcherServlet 是整個(gè) Web 應(yīng)用的控制器;
- 從微觀考慮,Controller 是單個(gè) Http 請求處理過程中的控制器;
- ModelAndView 是 HTTP 請求過程中返回的模型(Model)和視圖(View)。
1.5 HandlerInterceptor 接口:攔截器
1.6 ViewResolver 接口的實(shí)現(xiàn)類
Spring 提供的視圖解析器(ViewResolver)在 Web 應(yīng)用中查找 View 對象,從而將相應(yīng)結(jié)果渲染給客戶。
不同種類的 View 會對應(yīng)不同的 ViewResolver,例如:
- JSP 需要用到
org.springframework.web.servlet.view.InternalResourceViewResolver - 模板引擎需要用到
org.springframework.web.servlet.view.tiles3.TilesViewResolver - 文件下載需要用到
org.springframework.web.servlet.view.BeanNameViewResolver
1.7 View 接口
View 也會有不同的實(shí)現(xiàn)類,例如返回 JSP 的 View 時(shí)需要用到 org.springframework.web.servlet.view.JstlView。
1.8 LocalResolver 接口
1.9 HandlerExceptionResolver 接口:異常處理
1.10 ModelAndView 類
2. Spring 啟動過程
對于一個(gè) Web 應(yīng)用,其部署在 Web 容器中(例如 Tomcat),Web 容器提供其一個(gè)全局的上下文環(huán)境,這個(gè)上下文就是 ServletContext,其為后面的 Spring IoC 容器提供宿主環(huán)境。
在應(yīng)用 web.xml 中會提供有 ContextLoaderListener,例如:
<!--監(jiān)聽器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
在 Web 容器(例如 Tomcat)啟動時(shí),會觸發(fā)容器初始化事件,此時(shí) ContextLoaderListener 會監(jiān)聽到這個(gè)事件,其 contextInitialized 方法會被調(diào)用,在這個(gè)方法中,Spring 會初始化一個(gè)啟動上下文,這個(gè)上下文被稱為根上下文,即 WebApplicationContext,這是一個(gè)接口類,確切的說,其實(shí)際的實(shí)現(xiàn)類是 XmlWebApplicationContext。這個(gè)就是 Spring的 IoC 容器,其對應(yīng)的 Bean 定義的配置由 web.xml 中的 context-param 標(biāo)簽指定。
public void contextInitialized(ServletContextEvent event) {
this.initWebApplicationContext(event.getServletContext());
}
ContextLoaderListener 監(jiān)聽器初始化完畢后,開始初始化 web.xml 中配置的 Servlet,這個(gè) Servlet 可以配置多個(gè),以最常見的DispatcherServlet為例,這個(gè) Servlet 實(shí)際上是一個(gè)標(biāo)準(zhǔn)的前端控制器,用以轉(zhuǎn)發(fā)、匹配、處理每個(gè) Servlet 請求。
DispatcherServlet 上下文在初始化的時(shí)候會建立自己的 IoC 上下文,用以持有 Spring MVC 相關(guān)的 Bean。在建立 DispatcherServlet 自己的 IoC 上下文時(shí),會先從 ServletContext 中獲取之前的根上下文(WebApplicationContext)作為自己上下文的 parent 上下文。有了這個(gè) parent 上下文之后,再初始化自己持有的上下文。
當(dāng) Web 項(xiàng)目啟動時(shí),做初始化工作,所以我們大部分是配置在 web.xml 里面:
<web-app>
<display-name>Web Application</display-name>
<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>
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>springMVC_rest</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springMVC_rest</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
圖片引用自 http://www.itdecent.cn/p/dc64d02e49ac

圖片引用自 https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc

3. DispatcherServlet 初始化過程
DispatcherServlet 繼承了 FrameworkServlet,FrameworkServlet 繼承了 HttpServletBean,HttpServletBean 繼承了 HttpServlet 類。
第一步:HttpServletBean 類init() 方法
HttpServletBean 類有一個(gè)入口點(diǎn)就是重寫了 init 方法。
- 先通過
PropertyValues獲取web.xml文件init-param的參數(shù)值; - 然后通過
ResourceLoader讀取.xml配置信息; -
BeanWrapper對配置的標(biāo)簽進(jìn)行解析和將系統(tǒng)默認(rèn)的 bean 的各種屬性設(shè)置到對應(yīng)的 bean 屬性;
第二步:FrameworkServlet 類 initServletBean() 方法
initWebApplicationContext() 初始化上下文,并作為值放到了 ServletContext 里,因?yàn)椴煌?DispatherServlet 有對應(yīng)的各自的上下文,而且上下文有設(shè)置父上下文和 id 屬性等。
上下文項(xiàng)目啟動時(shí)會調(diào)用 createWebApplicationContext() 方法。
第三步:DispatcherServlet 類 onRefresh() 方法
DispatcherServlet 初始化各個(gè)功能的實(shí)現(xiàn)類。比如異常處理、視圖處理、請求映射處理等。
// 初始化上傳文件解析器
initMultipartResolver(context);
// 初始化本地解析器
initLocaleResolver(context);
// 初始化主題解析器
initThemeResolver(context);
// 初始化映射處理器
initHandlerMappings(context);
// 初始化適配器處理器
initHandlerAdapters(context);
// 初始化異常處理器
initHandlerExceptionResolvers(context);
// 初始化請求到視圖名翻譯器
initRequestToViewNameTranslator(context);
// 初始化視圖解析器
initViewResolvers(context);
4. DispatcherServlet 處理請求過程
圖片引用自 https://terasolunaorg.github.io/guideline/5.0.0.RELEASE/en/Overview/SpringMVCOverview.html

- 用戶向服務(wù)器發(fā)送請求,請求被 Spring 前置控制 Servelt
DispatcherServlet捕獲; -
DispatcherServlet對請求 URL 進(jìn)行解析,得到請求資源標(biāo)識符(URI)。然后根據(jù)該 URI,調(diào)用HandlerMapping獲得該Handler配置的所有相關(guān)的對象(包括Handler對象以及Handler對象對應(yīng)的攔截器),最后以HandlerExecutionChain對象的形式返回; -
DispatcherServlet根據(jù)請求獲得Handler,選擇一個(gè)合適的HandlerAdapter。(如果成功獲得HandlerAdapter后,此時(shí)將開始執(zhí)行攔截器的preHandler(...)方法) - 提取 Request 中的模型數(shù)據(jù),填充
Handler入?yún)ⅲ_始執(zhí)行Handler(Controller)。 在填充Handler的入?yún)⑦^程中,根據(jù)你的配置,Spring 將幫你做一些額外的工作:-
HttpMessageConveter:將請求消息(如 JSON、XML 等數(shù)據(jù))轉(zhuǎn)換成一個(gè)對象,將對象轉(zhuǎn)換為指定的響應(yīng)信息; - 數(shù)據(jù)轉(zhuǎn)換:對請求消息進(jìn)行數(shù)據(jù)轉(zhuǎn)換。如
String轉(zhuǎn)換成Integer、Double等; - 數(shù)據(jù)根式化:對請求消息進(jìn)行數(shù)據(jù)格式化。 如將字符串轉(zhuǎn)換成格式化數(shù)字或格式化日期等;
- 數(shù)據(jù)驗(yàn)證: 驗(yàn)證數(shù)據(jù)的有效性(長度、格式等),驗(yàn)證結(jié)果存儲到
BindingResult或Error中;
-
-
Handler執(zhí)行完成后,向DispatcherServlet返回一個(gè)ModelAndView對象; - 根據(jù)返回的
ModelAndView,選擇一個(gè)適合的ViewResolver返回給DispatcherServlet; -
ViewResolver結(jié)合Model和View,來渲染視圖; - 將渲染結(jié)果返回給客戶端;