SpringMVC原始碼系列:九大元件小記

glmapper發表於2018-03-22

前面幾篇文章都是針對於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的子類還沒有分析完,這個會後期更新的。

對於HandlerMapping來說,其作用就是根據request找到相應的處理器HandlerIntecepter攔截器。具體細節引數上面第一篇文章。

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原始碼系列中對各個元件的實現細節分析時再一探究竟吧。

相關文章