前面幾篇文章都是針對於SpringMVC中的具體元件進行原始碼分析的;本文主要用於補充記錄一下關於SpringMVC中九大元件的學習。這個會牽扯出除之前的幾篇HandlerMapping
之外的其他一些基礎元件。
之前簡單的有介紹過DispatcherServlet
這個類的體系結構,此處就不再贅述了。在DispatcherServlet
類中,其在mvc子容器進行初始化時就會完成對九大元件的初始化工作,具體哪九大元件後面會慢慢說到。先來看下在DispatcherServlet
中是通過哪些方法來完成初始化工作的,先貼一段程式碼:
protected void onRefresh(ApplicationContext context) {
this.initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
this.initMultipartResolver(context);
this.initLocaleResolver(context);
this.initThemeResolver(context);
this.initHandlerMappings(context);
this.initHandlerAdapters(context);
this.initHandlerExceptionResolvers(context);
this.initRequestToViewNameTranslator(context);
this.initViewResolvers(context);
this.initFlashMapManager(context);
}
複製程式碼
上面程式碼中的onRefresh
方法就是DispatcherServlet
的入口方法。在onRefresh
中又通過呼叫initStrategies
方法來將各個元件的初始化邏輯進行整合,個人理解其實就是策略套策略,在一個就是職責也明確。
在initStrategies
方法中又通過呼叫元件各自的初始化方法來完成具體的初始化工作。從這個地方其實就可以清楚的看出SpringMVC中的9個元件名稱了。下面就來捋一捋這九大元件的基本職責。
HandlerMapping
關於handlermapping在下面幾篇文章中做過一些基本介紹,但是還不是很全,對於handlermapping的子類還沒有分析完,這個會後期更新的。
- SpringMVC原始碼系列:HandlerMapping
- SpringMVC原始碼系列:AbstractHandlerMapping
- SpringMVC原始碼系列:AbstractUrlHandlerMapping
對於HandlerMapping
來說,其作用就是根據request
找到相應的處理器Handler
和Intecepter
攔截器。具體細節引數上面第一篇文章。
HandlerAdapter
如果說HandlerMapping
是一支筆,那麼HandlerAdapter
就是用筆的人。也就是說HandlerAdapter
就是使用處理器幹活的人。為什麼呢?來看下程式碼:
public interface HandlerAdapter {
boolean supports(Object var1);
ModelAndView handle(HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception;
long getLastModified(HttpServletRequest var1, Object var2);
}
複製程式碼
是不是一目瞭然了,在HandlerAdapter
介面中提供了handle
這樣一個方法,引數中Object handler第三個引數其實就是一個處理器,那我們就知道了,handle
方法就是使用handler
來處理邏輯的。處理之後返回一個ModelAndView
。
HandlerExceptionResolver
這個是SpringMVC
中的異常處理元件,HandlerExceptionResolver
這個元件的作用就是根據異常設定ModelAndView
,然後再將處理結果交給render
方法進行渲染。當然render
也僅僅只是負責將ModelAndView
渲染成頁面,ModelAndView
的具體來源它不關心。
這裡需要說明一下,加入在渲染過程中發生異常怎麼辦?從上面的分析我們可以清楚的知道,HandlerExceptionResolver
這個元件對異常的處理結果是ModelAndView
,然後再由render
方法進行渲染,也就是說HandlerExceptionResolver
是在渲染之前工作的,因此渲染過程中發生異常,HandlerExceptionResolver
是不會處理的。
public interface HandlerExceptionResolver {
ModelAndView resolveException(HttpServletRequest var1, HttpServletResponse var2, Object var3, Exception var4);
}
複製程式碼
在HandlerExceptionResolver
中也只有一個方法,這個方法就是從異常中解析出ModelAndView
。
ViewResolver
ViewResolver
的作用是將String
型別的邏輯檢視根據local解析為View
檢視的。下面是ViewResolver
的原始碼介面定義:
public interface ViewResolver {
View resolveViewName(String viewName, Locale local) throws Exception;
}
複製程式碼
從程式碼中可以看到,在ViewResolver
中也是隻有一個方法,從resolveViewName
方法的引數和返回結果就很好的解釋了其作用。
- viewName String型別的檢視名
- local 區域,可以用來做國際化。
View
實際上是用來渲染頁面的,也就是說將程式返回的結果填入到具體的模板裡面,生成具體的檢視檔案,比如:jsp,ftl,html等。
但是這裡又會牽扯出兩個問題:
- 用什麼模板?
- 引數怎麼填入?
當然,這兩個問題也就是本小節說的ViewResolver
需要解決的問題。大體分為兩種:
針對單一檢視型別的解析器
- InternalResourceViewResolver
- FreeMarkerViewResolver
上面兩種是用的最多的兩種,InternalResourceViewResolver
用來解析jsp,而FreeMarkerViewResolver
則是針對FreeMarker。
針對同時解析多種型別檢視的解析器
-
BeanNameViewResolver
需要同時使用檢視名和對應的local來解析檢視。它需要將每一個檢視名和對應的檢視型別配置到相應的properties檔案中。(後面講元件實現細節時給出列子)
-
XmlViewResolver
XmlViewResolver和BeanNameViewResolver有點差不多,BeanNameViewResolver使用的是xml格式的配置檔案。
-
ResourceBundleViewResolver
這個其實就是根據viewName從Spring容器中查詢bean,再根據這個bean來找到對應的檢視。
LocalResolver
在上面的ViewResolver
中提到,解析檢視需要兩個引數,一個是String型別的邏輯檢視名,另外一個是local。LocalResolver
的作用就是從request中解析出local的。
public interface LocaleResolver {
Locale resolveLocale(HttpServletRequest request);
void setLocale(HttpServletRequest request, HttpServletResponse response, Locale local);
}
複製程式碼
第一個方法是從request中解析出local,第二個方法是將local設定到request中。
關於local大多數情況下都是用來做國際化處理的。
ThemeResolver
解析主題的。這個我平時除了SpringMVC自己提供的功能外,很少自己去擴充套件使用,即使是換主題也沒有做過。不過既然存在肯定是有存在的原因的。對於我們常見的網頁介面活著手機介面來說,一套主題無非就是換一套圖片,活著css樣式檔案等等。我們通過ThemeResolver
這個就可以實現這樣的功能。具體使用其實也就是配一套properties檔案供系統在不同的時候讀取切換;當然使用這個也是可以實現國際化的。
public interface ThemeResolver {
String resolveThemeName(HttpServletRequest request);
void setThemeName(HttpServletRequest request, HttpServletResponse response, String themeName);
}
複製程式碼
RequestToViewNameTranslator
這個其實還是挺有意思的,就是將request請求轉換為檢視名稱。
public interface RequestToViewNameTranslator {
String getViewName(HttpServletRequest request) throws Exception;
}
複製程式碼
RequestToViewNameTranslator
只有一個預設的實現類DefaultRequestToViewNameTranslator
。
在DefaultRequestToViewNameTranslator
具體實現了getViewName(HttpServletRequest request)方法:
public String getViewName(HttpServletRequest request) {
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
return this.prefix + this.transformPath(lookupPath) + this.suffix;
}
複製程式碼
主要是委派給urlPathHelper幫助類得到請求的字尾名稱,比如通過 請求路徑比如/glmapper/login.do轉換得到/login.do ;具體怎麼轉換成檢視也會在後面的元件介紹中給出具體的例子。
MultipartResolver
這個相應小夥伴們也不陌生,做網站多多少少會涉及到檔案上傳。MultipartResolver
就是用來處理上傳請求的。其處理方式就是將request包裝成MultipartHttpServletRequest
。然後我們就可以用MultipartHttpServletRequest
這個直接呼叫getFile獲取的檔案了。
FlashMapManager
這個在redirect是進行引數傳遞需要用到。
public interface FlashMapManager {
FlashMap retrieveAndUpdate(HttpServletRequest request, HttpServletResponse response);
void saveOutputFlashMap(FlashMap flashMap, HttpServletRequest request, HttpServletResponse response);
}
複製程式碼
retrieveAndUpdate
這個方法是用來恢復引數的,對於恢復過的和超時的引數將都會被刪除掉。
saveOutputFlashMap
這個方法是用來儲存引數的。
FlashMapManager
的預設實現機制中引數的儲存是放在session中的。我之前在一個專案中就有遇到過這種情況,對於一些我們不想暴露在url中的引數,在進行請求轉發時,可以使用@RedirectAttributes將引數儲存,然後在下一個處理器中獲取到。
小結
本篇主要是來對九大元件做一個籠統的介紹,細節實現及案例均不涉及;在後續的SpringMVC原始碼系列中對各個元件的實現細節分析時再一探究竟吧。