[轉載]開發 Spring 自訂視圖和視圖解析器,spring視圖

來源:互聯網
上載者:User

[轉載]開發 Spring 自訂視圖和視圖解析器,spring視圖
原文出處

http://www.ibm.com/developerworks/cn/java/j-lo-springview/

概述

Spring 3.0 預設包含了多種視圖和視圖解析器,比如 JSP、Velocity 視圖等,但在某些情況下,我們需要開發自訂的視圖及其解析器,以便顯示特殊檔案格式的視圖,我們也可以使用自訂視圖及解析器,針對特定的視圖做相應的處理。本文將通過一個樣本來介紹如何開發 Spring 自訂視圖和視圖解析器,來顯示尾碼名為 SWF 的視圖,並提供一個簡單的註冊機制,為特定尾碼名的視圖註冊相應的視圖解析器。

Spring 視圖和視圖解析器簡介 什麼是 Spring 視圖和視圖解析器

Spring MVC(Model View Controller)是 Spring 中一個重要的組成部分,而 Spring 視圖和視圖解析器則是 Spring MVC 中的組成部分。在介紹 Spring 視圖和視圖解析器前,我們先瞭解下在 Spring MVC 架構中,一個 Web 請求所需經曆的六個階段:

圖 1.Spring MVC 處理流程

通過以上 Spring MVC 的介紹,我們可以發現,視圖和視圖解析器將出現在整個請求處理流程中的最後部分。那麼到底什麼是視圖和視圖解析器?簡而言之,視圖是指 Spring MVC 中的 V(View),而視圖解析器的功能則是依據指定的規則來尋找相應的視圖。

常用視圖和視圖解析器簡介

在開發中,視圖通常就是 JSP、Velocity 等。Spring 預設提供了多種視圖解析器,比如,我們可以使用最常用解析器 InternalResourceViewResolver 來尋找 JSP 視圖(與之相對應的視圖類為 InternalResourceView)。通常,一個視圖解析器只能尋找一個或多個特定類型的視圖,在遇到 Spring 不支援的視圖或者我們要自訂視圖尋找規則的情況下,我們就可以通過擴充 Spring 來自訂自己所需的視圖解析器。目前,視圖解析器都需要實現介面 org.springframework.web.servlet.ViewResolver, 它包含方法 resolveViewName,該方法會通過視圖名尋找並返回 Spring 視圖對象。表 1 列出了常用的 Spring 視圖解析器。

表 1.Spring 常用視圖解析器列表
   

XmlViewResolver
介面 ViewResolver 的實現,從 XML 設定檔中查詢檢視表實現(預設 XML 設定檔為 /WEB-INF/views.xml).

ResourceBundleViewResolver
介面 ViewResolver 的實現,用於從 properties 檔案中查詢檢視表。.

UrlBasedViewResolver
介面 ViewResolver 的實現,用於根據請求的 URL 路徑返回相應的視圖,該視圖需為抽象類別 AbstractUrlBasedView 的實現,它還有些子類,如 InternalResourceView 和 JstlView 等 .

InternalResourceViewResolver
UrlBasedViewResolver 的子類,通常用於尋找 JSP(類 InternalResourceView)和 JSTL(類 JstlView,InternalResourceView 的子類)等視圖。

VelocityViewResolver /FreeMarkerViewResolver
UrlBasedViewResolver 的子類分別用於支援 Velocity(類 VelocityView)和 FreeMark 視圖(類 FreeMarkerView)。

ContentNegotiatingViewResolver
介面 ViewResolver 的實現,用於根據請求檔案的尾碼名或請求的 header 中的 accept 欄位查詢檢視表。

在多數項目中,InternalResourceViewResolver 是最常用的,該解析器可以返回指定目錄下指定尾碼的檔案,它支援 JSP 及 JSTL 等視圖技術,但是用該視圖解析器時,需要注意設定好正確的優先順序,因為該視圖解析器即使沒有找到正確的檔案,也會返回一個視圖,而不是返回 null,這樣優先順序比該視圖解析器低的解析器,將不會被執行。

在 Web 開發中,我們的最上層顯示可以是 JSP、Excel、Velocity 等,在 Spring 中,不同的最上層顯示技術都有其對應的 Java 視圖類,正如表 1 所提到的,InternalResourceView 可以代表 JSP 視圖,FreeMarkerView 代表 FreeMarker 視圖。目前,Spring 支援多種技術開發的視圖,包括 JSP、JSTL、Excel,Velocity 等,在多數項目中,使用者並不需要自訂自己的視圖,在接下來的章節,我們將介紹如何定義開發視圖和視圖解析器。

樣本情境介紹

在多數項目中,我們並不需要開發定製視圖解析器和視圖,但在某些情況下,比如我們項目中的視圖格式並不是 Spring 所支援的,或者我們想使用自己的視圖和視圖解析器來更靈活的處理視圖,此時,我們就可以開發自己的視圖解析器和視圖。

在本例中,我們提供了三種視圖:JSP 檔案、SWF 檔案(flash 檔案)以及一個自訂尾碼名(.config)的檔案,為了更方便的支援 SWF 視圖和自訂檔案尾碼名的視圖,我們開發了自訂的視圖對象,希望能夠使用該視圖對象來支援 SWF 檔案和 .config 檔案,另外還開發了兩個視圖解析器來實現本例。

開發 Spring 視圖

在 Spring 中,所有的視圖類都需要實現介面 org.springframework.web.servlet.view.View, 2 所示,Spring 還提供了多個實現了 View 介面的抽象類別,所以我們並不需要直接實現介面 View, 而是可以實現 Spring 所提供的抽象類別。本例中的自訂視圖類便是繼承了抽象類別 org.springframework.web.servlet.view.AbstractUrlBasedView,通過繼承抽象類別可以減輕定製開發的複雜度。

圖 2.Spring 視圖介面繼承體系圖

為了簡化程式開發和相容更多的視圖,本例開發的自訂視圖類為一個通用視圖類,它可以將請求檔案的內容直接寫到請求響應(HTTP Response)中,並設定為該視圖類所配置的內容類型(HTTP content-type),但該視圖類只能用於處理瀏覽器能直接顯示的請求檔案資源,如文字檔、SWF 檔案等,但它並不能支援 JSP 等需要編譯處理的檔案。本例中,我們便是使用自訂視圖類 GenericFileView 來顯示 SWF 和 .config 文字檔。清單 1 給出了通用視圖類代碼。

清單 1. 通用視圖類代碼
public class GenericFileView extends AbstractUrlBasedView {  // 預設內容類型 private final static String CONTENT_TYPE = "text/plain";  //http response conent  private String responseContent;  public GenericFileView() {      super();      setContentType(CONTENT_TYPE);  }  @Override  public void setContentType(String contentType) {      super.setContentType(contentType);  }  @Override  protected void renderMergedOutputModel(Map<String, Object> model,        HttpServletRequest request, HttpServletResponse response)        throws Exception {      // 設定 content type      response.setContentType(getContentType());      // 寫入 response 內容     response.getWriter().write(this.responseContent);      response.getWriter().close();  }  /**  * 設定 http response content  * @param responseContent  */  public void setResponseContent(String responseContent) {      this.responseContent = responseContent;  }  }
正如前文所述,本例中的視圖類繼承了抽象類別 AbstractUrlBasedView,因此,我們的自訂通用視圖類並不需太多代碼,其代碼主要就是設定檔案內容類型和將響應的內容設定到請求響應對象中。
開發 Spring 視圖解析器

有了視圖類,我們還需要尋找該視圖類的視圖解析器。所有的視圖解析器都需要實現介面 org.springframework.web.servlet.ViewResolver,但同視圖的實現一樣,Spring 還提供了一個抽象類別,我們同樣可以通過實現抽象類別來節省開發工作。

在本例中,我們開發了自訂視圖解析器 GenericFileViewResolver,該類實現了抽象類別 org.springframework.web.servlet.view.AbstractCachingViewResolver,從圖 3 可以發現,常用的 Spring 中的視圖解析器都繼承了該抽象類別。

圖 3. 抽象類別 AbstractCachingViewResolver 繼承體系圖

除了繼承抽象類別 AbstractCachingViewResolver 外,本例中的視圖解析器還是實現了介面 org.springframework.core.Ordered,該介面用於設定視圖解析器的調用優先順序。這是因為,通常一個項目中會有多個視圖解析器,那麼就需要我們設定各個解析器被調用的優先順序,尤其是和 InternalResourceViewResolver 混用的情況下,必須要定義正確的調用優先順序,正如我們在前面提到的 InternalResourceViewResolver 會永遠返回一個視圖,即使在尋找不到合適的視圖的情況下也不會返回 null,導致後續的視圖解析器不會被調用。

視圖解析器的核心方法是 loadView,如清單 2 所示,在該方法中,需要根據請求的檔案路徑,找到該請求的檔案,然後再產生一個新的視圖,並將檔案流寫到視圖對象中。清單 2 為自訂的視圖解析器程式碼片段

清單 2. 自訂視圖解析器程式碼片段
@Override     protected View loadView(String viewName, Locale locale) throws Exception {         if (location == null) {             throw new Exception(                     "No location specified for GenericFileViewResolver.");         }         String requestedFilePath = location + viewName;         Resource resource = null;                try {             logger.finest(requestedFilePath);             resource = getApplicationContext().getResource(requestedFilePath);                    } catch (Exception e) {             // 返回 null, 以便被下一個 resolver 處理            logger.finest("No file found for file: " + requestedFilePath);             return null;         }         logger.fine("Requested file found: " + requestedFilePath + ",                     viewName:" + viewName);         // 根據視圖名,擷取相應的 view 對象        GenericFileView view = this.getApplicationContext().getBean(this.viewName,                 GenericFileView.class);         view.setUrl(requestedFilePath);         // 寫入 view 內容        view.setResponseContent(inputStreamTOString(resource.getInputStream(),                 "ISO-8859-1"));         return view;  }

需要注意的是,在找不到請求檔案的時候,需要返回 null,這樣 Spring 容器中所註冊的其他低優先順序的視圖解析器才能被調用。

開發複合視圖解析器

由於本例需要支援 SWF 及自訂尾碼名的檔案,所以我們期望能夠根據不同請求的尾碼名來調用不同的視圖解析器。實際上,Spring 已經提供了類似的視圖解析器-ContentNegotiatingViewResolver,它可以根據請求的檔案尾碼名或請求的 Accept 頭來查詢檢視表。ContentNegotiatingViewResolver 本身並不負責查詢檢視表,它只是將視圖尋找工作代理給所註冊的視圖解析器,清單 3 給出了 ContentNegotiatingViewResolver 的設定檔片段。

清單 3.ContentNegotiatingViewResolver
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">   <property name="mediaTypes">     <map>       <entry key="atom" value="application/atom+xml"/>       <entry key="html" value="text/html"/>       <entry key="json" value="application/json"/>     </map>   </property>   <property name="viewResolvers">     <list>       <bean class="org.springframework.web.servlet.view.BeanNameViewResolver"/>       <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">         <property name="prefix" value="/WEB-INF/jsp/"/>         <property name="suffix" value=".jsp"/>       </bean>     </list>   </property>   <property name="defaultViews">     <list>       <bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />     </list>   </property>  </bean>

從清單 3 可以發現,在使用 ContentNegotiatingViewResolver 時,一般需要配置三個部分:

關於 ContentNegotiatingViewResolver 的具體使用,讀者可以參見 Spring 官方文檔。

本例開發的複合視圖解析器和 ContentNegotiatingViewResolver 類似,雖然本例也可以使用 ContentNegotiatingViewResolver 來實現相同的功能,但 ContentNegotiatingViewResolver 較為複雜,它在查詢檢視表時,會將所有註冊到 ContentNegotiatingViewResolver 下的視圖解析器全部調用一遍,然後將所有尋找到的視圖儲存為候選視圖,最後再根據篩選條件,篩選出一個最為合適的視圖。而本例中的複合視圖解析器則簡單實用的多,只需要註冊檔案尾碼名和對應的視圖解析器即可,當為請求找到相應的視圖解析器時,便直接調用該視圖解析器,而不需調用其所註冊的所有視圖解析器,清單 4 給出了在 Spring 設定檔中對該複合視圖解析的設定檔片段。

清單 4. 複合視圖解析器配置片段
<bean id="viewResolver"          class="com.sample.web.viewresolver.MultipleViewResolver" p:order="0">         <property name="resolvers">             <map>                           <entry key="config">                     <bean class="com.sample.web.viewresolver.GenericFileViewResolver"                          p:location="/WEB-INF/config/" p:cache="false">                         <property name="viewName" value="configFileView"/>                     </bean>                 </entry>                          <entry key="swf">                     <bean class="com.sample.web.viewresolver.GenericFileViewResolver"                          p:location="/WEB-INF/swf/" p:cache="false">                         <property name="viewName" value="swfFileView"/>                     </bean>                 </entry>                             </map>         </property>     </bean>

在設定檔中,我們為不同的檔案尾碼名註冊了相應的視圖解析器,並為該視圖解析器配置了所對應尋找的視圖類。同 ContentNegotiatingViewResolver 類似,本例中的複合視圖解析器 MultipleViewResolver 也是將具體的視圖尋找工作代理給所註冊的視圖解析器,實際上,MultipleViewResolver 也是一個普通的視圖解析器,不過在核心方法 loadView(如清單 5)中,首先獲得請求視圖的尾碼名,然後根據尾碼名獲得所註冊的視圖解析器,最後,再使用獲得的視圖解析器查詢檢視表。

清單 5 .MultipleViewResolver 程式碼片段
@Override  protected View loadView(String viewName, Locale locale) throws Exception {  String fileExtension = StringUtils.getFilenameExtension(viewName);  // 返回 null 如果沒有尾碼名,以便下一個 resolver 處理 if (fileExtension == null) {  return null;  }  // 通過尾碼名擷取 resolver  ViewResolver resolver = resolvers.get(fileExtension);  //return null to invoke next resolver if no resolver found  return resolver == null ? null : resolver.resolveViewName(viewName,  locale);  }

在準備好了自訂視圖和視圖解析後,我們就可以開發 Spring MVC 中的控制器來處理 Web 請求。

開發控制器

在本例中,我們將要示範使用自訂視圖解析器處理 SWF 和 .config 檔案訪問請求,另外還提供了處理 JSP 檔案訪問請求的能力,所以,本例提供了一個簡單的控制器 SampleController,該控制器能夠處理三個請求路徑,並返回相應的視圖,如清單 6 所示。

清單 6 .SampleController 程式碼片段
@Controller  public class SampleController {  @RequestMapping("/jsp/sample")  public String getSampleJsp() {  return "SampleJsp";  }   @RequestMapping("/config/sample.config")  public String getSampleConfig() {  return "SampleConfig.config";  }    @RequestMapping("/swf/sample.swf")  public String getSampleSwf() {  return "SampleSwf.swf";  }   }

根據以上代碼,控制器將為請求 /jsp/sample 返回視圖 SampleJsp,為請求 /config/sample.config 返回視圖 SampleConfig.config,並能夠為請求 /swf/sample.swf 返回視圖 SampleSwf.swf。

配置視圖解析器

在完成控制器的開發後,我們就可以配置視圖及相應的解析器。在開發中,我們通常會將 Spring 的設定檔至少分為兩個部分,在本例中,我們建立了兩個 Spring 設定檔 :spring-root.xml 和 spring-web.xml, 4 所示。spring-root.xml 用於存放非 web 管理相關的配置,而 spring-web.xml 則存放了所有與 web 相關的配置。由於本例比較簡單,spring-root.xml 檔案中並沒有具體的配置內容。

圖 4. 樣本專案檔結構圖

通過清單 6 可以發現,我們使用了註解(Annotation)來定義控制器,所以,我們需要在 spring-web.xml 中配置 Spring 掃描的根目錄,如清單 7 所示。

清單 7 設定 Spring 註解掃描路徑
<context:component-scan base-package="com.sample.web.controller"/>

清單 4 中配置了複合視圖解析器,除此之外,我們還需要定義解析器 jspViewResolver 來尋找 JSP 檔案,注意 order 屬性值為 1,大於複合視圖解析器的屬性值,這樣,我們就可以首先使用自訂的複合視圖解析器查詢檢視表,當沒有找到合適的代理視圖解析器或視圖時,才會調用 jspViewResolver 來進行視圖尋找。

清單 8. 配置 InternalResourceViewResolver
<!-- View Resolver for JSP files -->  <bean id="jspViewResolver"          class="org.springframework.web.servlet.view.InternalResourceViewResolver"          p:order="1">         <property name="prefix" value="/WEB-INF/jsp/"/>         <property name="suffix" value=".jsp"/>  </bean>

在清單 4 中,我們還定義了所註冊的視圖解析器所對應的視圖,所以,在設定檔中,我們還需要聲明相關的視圖,如清單 9 所示。

清單 9. 配置自訂視圖
<bean id="configFileView" class="com.sample.web.view.GenericFileView"          p:contentType="text/plain" p:url="" scope="prototype"/>  <bean id="swfFileView" class="com.sample.web.view.GenericFileView"          p:contentType="application/x-shockwave-flash" p:url="" scope="prototype"/>

configFileView 用於表示一個文字檔視圖,本例對應一個尾碼名為 .config 的文字檔,swfFileView 用於表示一個 flash 檔案視圖,本例為一個尾碼名為 .SWF 的 flash 檔案。

樣本運行

至此,本例的開發工作就都已經完成了,樣本控制器只支援三個請求路徑,但實際上,我們可以通過使用 @RequestMapping(value = "/path/{fileName}") 能夠讓控制器處理更為通用的請求。

在運行樣本前,需要準備好 Web 服務器和 Spring 運行環境,本例已經在 Tomcat 7.0 和 Spring 3.0.5 環境上測試通過,讀者可以下載原始碼部署在自己的環境上。

當我們輸入請求地址 http://localhost:8080/SpringSample/swf/sample.swf, 控制器將通過使用我們自訂的視圖解析器,尋找到視圖檔案 SampleSwf.swf,並將檔案內容作為 application/x-shockwave-flash 顯示到瀏覽器中, 5 所示。

圖 5. 請求訪問 SWF 檔案

處理自訂檔案尾碼名 .config 的處理流程同處理 SWF 檔案類似,差別之處在於,視圖的檔案媒體類型為 text/plain,所以在瀏覽器中,我們將會看到一個文本顯示, 6 所示 .

圖 6. 請求訪問 .config 檔案

另外,本例還支援 JSP 檔案的訪問,在本例中,當有 JSP 訪問請求時,自訂的視圖解析器將會被調用,但由於我們並沒有註冊尋找空尾碼名或 .jsp 請問的訪問,所以自訂的視圖解析器將返回一個 null,接下來,Spring 會接著調用我們登入的 jspViewResolver(也就是 InternalResourceViewResolver)來繼續視圖尋找,並最終返回所請求的 JSP 檔案, 7 所示。

圖 7. 請求訪問 JSP 檔案 總結

本文介紹了 Spring 視圖和視圖解析器的相關概念,並簡單介紹了 Spring 所提供的視圖和視圖解析器,並結合樣本介紹了如何開發自訂的視圖和視圖解析器。

下載

SpringSample.zip

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.