Spring MVC之初始化

什么是SpringMVC?SpringMVC是一個(gè)基于Java的實(shí)現(xiàn)了MVC設(shè)計(jì)模式的請求驅(qū)動類型的輕量級Web框架,通過把Model,View,Controller分離,將web層進(jìn)行職責(zé)解耦,把復(fù)雜的web應(yīng)用分成邏輯清晰的幾部分,簡化開發(fā),減少出錯(cuò),方便組內(nèi)開發(fā)人員之間的配合。
工作流程
大致流程如下:
用戶發(fā)起請求一個(gè)url到中央控制器
中央控制器接收到請求后調(diào)用處理器映射器以獲取相應(yīng)的處理器(即controller)
處理器映射器返回處理器的位置給中央控制器
中央控制器調(diào)用處理器適配器獲取到指定的處理器(即controller)
處理器適配器把請求交給指定的處理器執(zhí)行請求
處理器執(zhí)行完業(yè)務(wù)邏輯后,返回?cái)?shù)據(jù)和視圖給處理器適配器
處理器適配器把數(shù)據(jù)和視圖返回給中央處理器
中央處理器請求視圖解析器進(jìn)行視圖解析
視圖解析器解析相應(yīng)的視圖并返回給中央控制器
中央控制器通過view將數(shù)據(jù)和模型填充到視圖中
view渲染完視圖后返回給中央控制器
中央控制器把結(jié)果響應(yīng)給用戶

九大組件
MultipartResolver:文件處理器,用于處理上傳請求,通過將普通的 Request 包裝成MultipartHttpServletRequest來實(shí)現(xiàn)。MultipartHttpServletRequest 可以通過 getFile() 直接獲得文件,如果是多個(gè)文件上傳,還可以通過調(diào)用 getFileMap 得到 Map<FileName, File> 這樣的結(jié)構(gòu)。MultipartResolver 的作用就是用來封裝普通的 request,使其擁有處理文件上傳的功能。
LocaleResolver: 當(dāng)前環(huán)境處理器,ViewResolver的 resolveViewName()方法,需要兩個(gè)參數(shù)。那么第 二個(gè)參數(shù) Locale 是從哪來的呢,這就是 LocaleResolver 要做的事了。LocaleResolver 用于從 request 中解析出 Locale, 在中國大陸地區(qū),Locale 當(dāng)然就會是 zh-CN 之類, 用來表示一個(gè)區(qū)域。這個(gè)類也是 i18n 的基礎(chǔ)。
ThemeResolver:主題處理器,主題,就是樣式,圖片以及它們所形成的 顯示效果的集合。Spring MVC 中一套主題對應(yīng)一個(gè) properties 文件,里面存放著跟當(dāng) 前主題相關(guān)的所有資源,如圖片,css 樣式等。創(chuàng)建主題非常簡單,只需準(zhǔn)備好資源,然 后新建一個(gè) "主題名.properties" 并將資源設(shè)置進(jìn)去,放在 classpath 下,便可以在頁面中跟主題有關(guān)的類有 ThemeResolver, ThemeSource 和 Theme。ThemeResolver 負(fù)責(zé)從 request 中解析出主題名, ThemeSource 則根據(jù)主 題名找到具體的主題, 其抽象也就是 Theme, 通過 Theme 來獲取主題和具體的資源。
HandlerMApping:處理器映射器,是用來查找 Handler 的,也就是處理器,具體的表現(xiàn)形式可以是類也可以是方法。比如,標(biāo)注了@RequestMApping 的每個(gè) method 都可以看成是一個(gè) Handler,由 Handler 來負(fù)責(zé)實(shí)際的請求處理。HandlerMApping 在請求到達(dá)之后, 它的作用便是找到請求相應(yīng)的處理器 Handler 和 Interceptors。
HandlerAdapters:處理器適配器,因?yàn)?Spring MVC 中 Handler 可以是任意形式的,只要能夠處理請求便行, 但是把請求交給 Servlet 的時(shí)候,由于 Servlet 的方法結(jié)構(gòu)都是如 doService(HttpServletRequest req, HttpServletResponse resp) 這樣的形式,讓固定的 Servlet 處理方法調(diào)用 Handler 來進(jìn)行處理,這一步工作便是 HandlerAdapter 要做的事。
HandleExceptionResolvers:異常處理器,用來處理 Handler 過程中產(chǎn)生的異常情況的組件。具體來說,此組件的作用是根據(jù)異常設(shè)置 ModelAndView, 之后再交給 render()方法進(jìn)行渲染 ,而 render() 便將 ModelAndView 渲染成頁面。不過有一點(diǎn),HandlerExceptionResolver 只是用于解析對請求做處理階段產(chǎn)生的異常,而渲染階段的異常則不歸他管了,這也是 Spring MVC 組件設(shè)計(jì)的一大原則分工明確互不干涉。
RequestToViewNameTranslator:視圖名稱翻譯器,從 Request 中獲取 viewName.。因?yàn)?ViewResolver 是根據(jù) ViewName 查找View, 但有的 Handler 處理完成之后,沒有設(shè)置 View 也沒有設(shè)置 ViewName, 便要通過這個(gè)組件來從 Request 中查找 viewName。
ViewResolvers:頁面渲染處理器,因?yàn)橥ǔT?SpringMVC 的配置文件中, 都會配上一個(gè)該接口的實(shí)現(xiàn)類來進(jìn)行視圖的解析。這個(gè)組件的主要作用,便是將 String 類型的視圖名和Locale解析為View類型的視圖。這個(gè)接口只有一個(gè)resolveViewName() 方法。從方法的定義就可以看出,Controller 層返回的 String 類型的視圖名 viewName, 最終會在這里被解析成為 View。View 是用來渲染頁面的,也就是說,它會將程序返回的參數(shù)和數(shù)據(jù)填入模板中,最終生成 html 文件。ViewResolver 在這個(gè)過程中,主要做兩件大事,即,ViewResolver 會找到渲染所用的模板(使用什么模板來渲染?)和所用的技術(shù)(其實(shí)也就是視圖的類型,如 JSP )填入?yún)?shù)。默認(rèn)情況下,Spring MVC 會為我們自動配置一個(gè) InternalResourceViewResolver,這個(gè)是針 對 JSP 類型視圖的。
FlashMapManager:參數(shù)傳遞管理器說到 FlashMapManager,就得先提一下 FlashMap。FlashMap 用于重定向 Redirect 時(shí)的參數(shù)數(shù)據(jù)傳遞,比如,在處理用戶訂單提交時(shí),為了避免重復(fù)提交,可以處理完 post 請求后 redirect 到一個(gè) get 請求,這個(gè) get 請求可以 用來顯示訂單詳情之類的信息。這樣做雖然可以規(guī)避用戶刷新重新提交表單的問題,但是在這個(gè)頁面上要顯示訂單的信息,那這些數(shù)據(jù)從哪里去獲取呢,因?yàn)?redirect 重定向是沒有傳遞參數(shù)這一功能的,如果不想把參數(shù)寫進(jìn) url(其實(shí)也不推薦這么做,url 有長度限制不說,把參數(shù)都直接暴露,感覺也不安全), 那么就可以通過 flashMap 來傳遞。只需要在 redirect 之前,將要傳遞的數(shù)據(jù)寫入 request( 可以通過 ServletRequestAttributes.getRequest() 獲 得)的屬性 OUTPUT_FLASH_MAP_ATTRIBUTE 中,這樣在 redirect 之后的 handler 中 Spring 就會自動將其設(shè)置到 Model 中,在顯示訂單信息的頁面上,就可以直接從 Model 中取得數(shù)據(jù)了。而 FlashMapManager 就是用來管理 FlashMap 的。
demo
下面這個(gè)圖是我從網(wǎng)上找的一個(gè)入門配置,DispatcherServlet在SpringMVC中是集中訪問點(diǎn),負(fù)責(zé)分派調(diào)度工作。因此我們需要告訴Web容器,我們將使用DispatcherServlet,并將URL映射到DispatcherServlet。在這里我們會通過“init-param”配置項(xiàng)進(jìn)行初始一些參數(shù),比如:HandlerMApping類、HandlerAdapter、ViewResolver等。接下來我們會講解MVC是如何初始化的。

注意:本文是以5.2.3版本為講解。
步驟一:初始化

通過DispatcherServlet類圖,看到他繼承HttpServlet類,我們上學(xué)時(shí)學(xué)過Servlet類的初始化方法是init()方法,然后我們發(fā)現(xiàn)其iinit方法其實(shí)在父類HttpServletBean中。大致業(yè)務(wù)如下:
從初始化參數(shù)設(shè)置 bean 屬性
委派給子類進(jìn)行初始化

步驟二:初始化WebApplicationContext
HttpServletBean的重寫方法,在設(shè)置任何bean屬性后調(diào)用。創(chuàng)建當(dāng)前servlet的WebApplicationContext。

步驟三:初始化IOC容器
這個(gè)方法大致業(yè)務(wù)如下:
初始化IOC容器(比如)
調(diào)用onRefresh:刷新上下文,這個(gè)方法是一個(gè)模板方法,被子類重寫來添加特定于servlet的業(yè)務(wù)。

步驟四:初始化策略對象
在這個(gè)方法里會把九大組件進(jìn)行初始化,在這里我們著重看下initHandlerMApping方法,在這里會初始化url與controller的關(guān)系。

步驟五:初始化HandlerMAppings
如果BeanFactory中沒有在namespace指定HandlerMApping,則默認(rèn)為BeanNameUrlHandlerMApping。

步驟六:初始化容器
BeanNameUrlHandlerMApping類繼承AbstractDectingUrlHandlerMApping類,在這個(gè)類里有個(gè)“initApplicationContext”的方法,業(yè)務(wù)邏輯如下:
initAppliicationContext:初始化父類容器,諸如各種攔截器
detectHandlers:注冊在當(dāng)前 ApplicationContext 中找到的所有url和controller的對應(yīng)關(guān)系

步驟七:注冊當(dāng)前ApplicationContext中找到的所有url和controller的對應(yīng)關(guān)系
業(yè)務(wù)邏輯如下:
obtainApplicationContext:獲取當(dāng)前ApplicationContext
獲取ApplicationContext容器中所有的beanName
determineUrlsForHandler:確定給定處理程序 bean 的 URL,該方法在當(dāng)前類是一個(gè)抽象方法,具體是由BeanNameUrlHandlerMApping類來實(shí)現(xiàn)的
registerHandler:為指定的 URL 集合注冊指定的處理器

步驟八:確定指定處理程序bean的URL

步驟九:循環(huán)遍歷URL注冊到指定的處理器

步驟十:為給定的URL注冊指定的處理器
這個(gè)方法很簡單,就是把給定的URL注冊到handlerMap中

時(shí)序圖

寫在最后
好兄弟可以點(diǎn)贊并關(guān)注我的公眾號“javaAnswer”,全部都是干貨。
