1. 程式人生 > 其它 >Spring MVC檢視解析器

Spring MVC檢視解析器

Spring MVC的檢視解析器

什麼是檢視解析器

在我們學習Spring MVC時,當我們傳送請求給Spring MVC控制的資源時被DispatcherServlet處理,Spring會分析看所有的HandleMapping中定義的請求對映中最合理的那個Handle,並通過HandleMapping得到該Handle,交給HandleAdapter處理Handle並返回一個ModelAndView物件。獲取到ModelAndView物件後,Spring會把View渲染交給使用者。在渲染View的過程中,發揮作用的就是ViewResolver和View。有時ModelAndView中不包含真正的檢視,而是一個邏輯檢視名的時候,ViewResolver就會根據規則將邏輯檢視名解析成真正的View物件,View物件才是真正進行渲染的

,把結果返回給瀏覽器。

通過上面的流程解釋,我們直到ViewResolverView是Spring MVC檢視解析的中最重要的介面。Spring MVC給我們提供了非常多的檢視解析器,我們可以先講一下什麼是檢視?

View介面的重要實現類

檢視基礎介面,它的各種實現類是無狀態的,因此是執行緒安全的

在此簡單介紹一下其中兩個重要的View實現類:
其中的renderMergedOutputModel是AbstractView抽象類中的重寫方法,實際上就是給View的render提供服務,render才是View物件的渲染。

重定向:RedirectView

這個檢視跟重定向有關,也是重定向問題的核心。
首先通過原始碼看渲染過程:

protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws IOException {
        String targetUrl = this.createTargetUrl(model, request);
        targetUrl = this.updateTargetUrl(targetUrl, model, request, response);
        FlashMap flashMap = RequestContextUtils.getOutputFlashMap(request);
        if (!CollectionUtils.isEmpty(flashMap)) {
            UriComponents uriComponents = UriComponentsBuilder.fromUriString(targetUrl).build();
            flashMap.setTargetRequestPath(uriComponents.getPath());
            flashMap.addTargetRequestParams(uriComponents.getQueryParams());
            FlashMapManager flashMapManager = RequestContextUtils.getFlashMapManager(request);
            if (flashMapManager == null) {
                throw new IllegalStateException("FlashMapManager not found despite output FlashMap having been set");
            }

            flashMapManager.saveOutputFlashMap(flashMap, request, response);
        }

        this.sendRedirect(request, response, targetUrl, this.http10Compatible);
    }

看到String targetUrl = this.createTargetUrl(model, request);這個作用就是建立Url重定向的路徑。

最重要的來了,看到最後一行this.sendRedirect(request, response, targetUrl, this.http10Compatible);,原來最後是使用了sendRedirect重定向到targetUrl

其中還有重要的就是構造路徑的過程,有興趣可以去研究原始碼,有需求可以在重定向時傳遞資料,在url中顯示,這個就是在構造路徑中實現的。

請求轉發:InternalResourceView

檢視渲染過程:

protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
        this.exposeModelAsRequestAttributes(model, request);
        this.exposeHelpers(request);
        String dispatcherPath = this.prepareForRendering(request, response);
        RequestDispatcher rd = this.getRequestDispatcher(request, dispatcherPath);
        if (rd == null) {
            throw new ServletException("Could not get RequestDispatcher for [" + this.getUrl( ) + "]: Check that the corresponding file exists within your web application archive!");
        } else {
            if (this.useInclude(request, response)) {
                response.setContentType(this.getContentType());
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Including resource [" + this.getUrl() + "] in InternalResourceView '" + this.getBeanName() + "'");
                }

                rd.include(request, response);
            } else {
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Forwarding to resource [" + this.getUrl() + "] in InternalResourceView '" + this.getBeanName() + "'");
                }

                rd.forward(request, response);
            }

        }
    }

咱們暫時只要關注兩個點即可rd.include(request, response);rd.forward(request, response);這個兩個方法,根據判斷條件來決定執行哪個方法到指定的jsp頁面。

ViewResolver檢視直譯器介面

ViewResolver介面中只有一個方法:
View resolveViewName(String viewName, Locale locale) throws Exception;
根據檢視名稱viewName和Local物件來獲取View物件,這就是檢視直譯器的作用。
那我們來解釋ViewResolver介面的一個常用的實現類InternalResourceViewResolver

InternalResourceViewResolver

繼承自UrlBasedViewResolver,以InternalResourceView作為檢視,若專案中存在“javax.servlet.jsp.jstl.core.Config”該類,那麼會以JstlView作為檢視。重寫了buildView方法,主要就是為了給InternalResourceView檢視設定屬性。

所以我們從中得知InternalResourceViewResolver是以InternalResourceView作為檢視也就是說它只能處理請求轉發,處理不了重定向! 然後我們再看看UrlBasedViewResolver是什麼?

UrlBasedViewResolver

UrlBasedViewResolver繼承自AbstractCachingViewResolver抽象類、並實現Ordered介面的類

AbstractCachingViewResolver抽象類:

  1. AbstractCachingViewResolver是一個抽象類,這種檢視解析器會把它曾經解析過的檢視儲存起來,然後每次要解析檢視的時候先從快取裡面找,如果找到了對應的檢視就直接返回,如果沒有就建立一個新的檢視物件,然後把它放到一個用於快取的map中,接著再把新建的檢視返回。使用這種檢視快取的方式可以把解析檢視的效能問題降到最低。
  2. AbstractCachingViewResolver是帶有快取功能的ViewResolver介面基礎實現抽象類,該類有個屬性名為viewAccessCache的以 "viewName_locale" 為key, View介面為value的Map。該抽象類實現的resolveViewName方法內部會呼叫createView方法,方法內部會呼叫loadView抽象方法。

Ordered介面
實現了相同介面的實現類優先順序問題,也就是將相同介面的實現類進行排序。後期學習工作原理的時候就會理解透徹。

OK瞭解UrlBasedViewResolver過後,我們再來看看他的屬性和方法

  • viewClass 檢視的型別
  • prefix 檢視名稱的字首
  • suffix 檢視名稱的字尾

這三屬性比較重要,檢視的型別能理解,但是字首和字尾是什麼意思?
這個時候就能體現UrlBasedViewResolver的重要性了,當我們根據請求得到ModelAndView時,可能只包含的邏輯路徑並不是完整的,比如真實路徑是/a/b/c/d/e.jsp,但是指定viewName時卻是“e”,這個時候UrlBasedViewResolver的字首字尾就發揮作用了:字首+viewName+字尾,所以字首就可以設定為/a/b/c/d/而後綴就可以設為.jsp 這樣就解決了這個問題,同時在遇到多個jsp檔案放在WEB-INF的多級目錄下,這個時候作用很大,節省了程式碼,但是必須指定viewClass(檢視型別)
可以通過看原始碼的createView()核心方法來理解產生View的原理

大致一眼看去主要產生的View的型別有兩種,一個是以"redirect:"為字首,型別是RedirectView。另一種是以"forward:"為字首,型別是InternalResourceView。


而我們會常用InternalResourceViewResolver這個檢視解析器,它繼承了UrlBasedViewResolver,看初始化的原始碼:

其中無參構造器中設定了Class<?> viewClass=this.requiredViewClass();

protected Class<?> requiredViewClass() {
    return InternalResourceView.class;
}

不難看出InternalResourceViewResolver已經規定了它只能處理InternalResourceView型別的檢視,也就是隻能解析請求轉發,而重定向則不能。有參構造也就是設定了字首和字尾。使用InternalResourceViewResolver的案例就不演示了,網上一大把。就是記錄一下學習檢視解析器的筆記。

總結

檢視解析器是Spring MVC為之重要的一個環節,主要是依靠ViewResolverView

  • ViewResolver根據ModelAndView的邏輯檢視名解析出View物件
  • View則將檢視渲染返回給瀏覽器呈現給使用者

檢視解析器的表層含義也就理解到這裡,更深層次的理解也需要更深入原始碼。有什麼錯誤請指出。