1. 程式人生 > >SpringMVC中的九大元件的理解和原始碼

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);
}