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.reibang.com/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é)果返回給客戶端芍殖;