SpringMVC中的九大元件的理解和原始碼
SpringMVC中的Servlet一共有三個層次,分別是HttpServletBean、FrameworkServlet和 DispatcherServlet。
HttpServletBean直接繼承自java的HttpServlet,其作用是將Servlet中配置的引數設定到相應的屬性;
FrameworkServlet初始化了WebApplicationContext;
DispatcherServlet初始化了自身的9個元件。
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個元件名稱了。
【1. HandlerMapping】
是用來查詢Handler的。在SpringMVC中會有很多請求,每個請求都需要一個Handler處理,具體接收到一個請求之後使用哪個Handler進行處理呢?這就是HandlerMapping需要做的事。對於 HandlerMapping
request
找到相應的處理器 Handler
和 Intecepter
攔截器
【2. HandlerAdapter】
從名字上看,它就是一個介面卡。因為SpringMVC中的Handler可以是任意的形式,只要能處理請求就ok,但是Servlet需要的處理方法的結構卻是固定的,都是以request和response為引數的方法。如何讓固定的Servlet處理方法呼叫靈活的Handler來進行處理呢?這就是HandlerAdapter要做的事情。
小結:Handler是用來幹活的工具;HandlerMapping用於根據需要乾的活找到相應的工具;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
【3. HandlerExceptionResolver】
其它元件都是用來幹活的。在幹活的過程中難免會出現問題,出問題後怎麼辦呢?這就需要有一個專門的角色對異常情況進行處理,在SpringMVC中就是HandlerExceptionResolver。具體來說,此元件的作用是根據異常設定ModelAndView,之後再交給render方法進行渲染。
public interface HandlerExceptionResolver {
ModelAndView resolveException(HttpServletRequest var1, HttpServletResponse var2, Object var3, Exception var4);
}
在 HandlerExceptionResolver
中也只有一個方法,這個方法就是從異常中解析出 ModelAndView
【4. ViewResolver】
ViewResolver用來將String型別的檢視名和Locale解析為View型別的檢視。View是用來渲染頁面的,也就是將程式返回的引數填入模板裡,生成html(也可能是其它型別)檔案。這裡就有兩個關鍵問題:使用哪個模板?用什麼技術(規則)填入引數?這其實是ViewResolver主要要做的工作,ViewResolver需要找到渲染所用的模板和所用的技術(也就是檢視的型別)進行渲染,具體的渲染過程則交由不同的檢視自己完成。
public interface ViewResolver {
View resolveViewName(String viewName, Locale local) throws Exception;
}
View
實際上是用來渲染頁面的,也就是說將程式返回的結果填入到具體的模板裡面,生成具體的檢視檔案,比如:jsp,ftl,html等。
但是這裡又會牽扯出兩個問題:
- 用什麼模板?
- 引數怎麼填入?
當然,這兩個問題也就是本小節說的 ViewResolver
需要解決的問題。大體分為兩種:
針對單一檢視型別的解析器
- InternalResourceViewResolver
- FreeMarkerViewResolver
上面兩種是用的最多的兩種, InternalResourceViewResolver
用來解析jsp,而 FreeMarkerViewResolver
則是針對FreeMarker。
針對同時解析多種型別檢視的解析器
-
BeanNameViewResolver
需要同時使用檢視名和對應的local來解析檢視。它需要將每一個檢視名和對應的檢視型別配置到相應的properties檔案中。
-
XmlViewResolver
XmlViewResolver和BeanNameViewResolver有點差不多,BeanNameViewResolver使用的是xml格式的配置檔案。
-
ResourceBundleViewResolver
這個其實就是根據viewName從Spring容器中查詢bean,再根據這個bean來找到對應的檢視。
【5. RequestToViewNameTranslator】
ViewName是根據ViewName查詢View,但有的Handler處理完後並沒有設定View也沒有設定ViewName,這時就需要從request獲取ViewName了,如何從request中獲取ViewName就是RequestToViewNameTranslator要做的事情了。RequestToViewNameTranslator在Spring MVC容器裡只可以配置一個,所以所有request到ViewName的轉換規則都要在一個Translator裡面全部實現。
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 ;
【6. LocaleResolver】
解析檢視需要兩個引數:一是檢視名,另一個是Locale。檢視名是處理器返回的,Locale是從哪裡來的?這就是LocaleResolver要做的事情。LocaleResolver用於從request解析出Locale,Locale就是zh-cn之類,表示一個區域,有了這個就可以對不同區域的使用者顯示不同的結果。SpringMVC主要有兩個地方用到了Locale:一是ViewResolver檢視解析的時候;二是用到國際化資源或者主題的時候。
public interface LocaleResolver {
Locale resolveLocale(HttpServletRequest request);
void setLocale(HttpServletRequest request, HttpServletResponse response, Locale local);
}
【7. ThemeResolver】
用於解析主題。SpringMVC中一個主題對應一個properties檔案,裡面存放著跟當前主題相關的所有資源、如圖片、css樣式等。SpringMVC的主題也支援國際化,同一個主題不同區域也可以顯示不同的風格。SpringMVC中跟主題相關的類有 ThemeResolver、ThemeSource和Theme。主題是通過一系列資源來具體體現的,要得到一個主題的資源,首先要得到資源的名稱,這是ThemeResolver的工作。然後通過主題名稱找到對應的主題(可以理解為一個配置)檔案,這是ThemeSource的工作。最後從主題中獲取資源就可以了。
public interface ThemeResolver {
String resolveThemeName(HttpServletRequest request);
void setThemeName(HttpServletRequest request, HttpServletResponse response, String themeName);
}
【8. MultipartResolver】
用於處理上傳請求。處理方法是將普通的request包裝成MultipartHttpServletRequest,後者可以直接呼叫getFile方法獲取File,如果上傳多個檔案,還可以呼叫getFileMap得到FileName->File結構的Map。此元件中一共有三個方法,作用分別是判斷是不是上傳請求,將request包裝成MultipartHttpServletRequest、處理完後清理上傳過程中產生的臨時資源。
【9. FlashMapManager】
用來管理FlashMap的,FlashMap主要用在redirect中傳遞引數。
retrieveAndUpdate
這個方法是用來恢復引數的,對於恢復過的和超時的引數將都會被刪除掉。
saveOutputFlashMap
這個方法是用來儲存引數的。
FlashMapManager
的預設實現機制中引數的儲存是放在session中的。我之前在一個專案中就有遇到過這種情況,對於一些我們不想暴露在url中的引數,在進行請求轉發時,可以使用@RedirectAttributes將引數儲存,然後在下一個處理器中獲取到。
public interface FlashMapManager {
FlashMap retrieveAndUpdate(HttpServletRequest request, HttpServletResponse response);
void saveOutputFlashMap(FlashMap flashMap, HttpServletRequest request, HttpServletResponse response);
}