天天看點

《Spring 5 官方文檔》18. Web MVC 架構(六)

在以前的spring版本中,使用者需要<code>handlermapping</code>在web應用程式上下文中定義一個或多個 bean,以将傳入的web請求映射到适當的處理程式。通過引入注釋控制器,您通常不需要這樣做,因為它<code>requestmappinghandlermapping</code>會<code>@requestmapping</code>自動在所有<code>@controller</code>bean 上查找 注釋。但是,請記住,所有<code>handlermapping</code>擴充的類<code>abstracthandlermapping</code>都具有以下可用于自定義行為的屬性:

<code>defaulthandler</code> 當這個處理程式映射不會導緻一個比對的處理程式時,使用預設處理程式。

<code>order</code>基于order屬性的值(參見 <code>org.springframework.core.ordered</code>接口),spring會排序上下文中可用的所有處理程式映射,并應用第一個比對處理程式。

<code>alwaysusefullpath</code>如果<code>true</code>spring使用目前servlet上下文中的完整路徑來找到一個适當的處理程式。如果<code>false</code>(預設值),則使用目前servlet映射中的路徑。例如,如果使用servlet <code>/testing/*</code>并将<code>alwaysusefullpath</code>屬性設定為true, <code>/testing/viewpage.html</code>則使用該屬性,而如果該屬性設定為false<code>/viewpage.html</code>。

<code>urldecode</code>預設為<code>true</code>,從spring 2.5開始。如果您喜歡比較編碼路徑,請将此标志設定為<code>false</code>。但是,<code>httpservletrequest</code>始終以解碼形式公開servlet路徑。請注意,與編碼路徑相比,servlet路徑将不比對。

以下示例顯示如何配置攔截器:

spring的處理程式映射機制包括處理程式攔截器,當您希望将特定功能應用于某些請求時,例如,檢查主體,這是有用的。

位于處理程式映射中的攔截器必須<code>handlerinterceptor</code>從 <code>org.springframework.web.servlet</code>包中實作。這個接口定義了三個方法: <code>prehandle(..)</code>被稱為前被執行的實際處理程式; <code>posthandle(..)</code>被稱為後執行的處理程式; 并在完成請求完成後<code>aftercompletion(..)</code>調用。這三種方法應提供足夠的靈活性進行各種預處理和後處理。

該<code>prehandle(..)</code>方法傳回一個布爾值。您可以使用此方法來中斷或繼續處理執行鍊。當此方法傳回<code>true</code>時,處理程式執行鍊将繼續; 當它傳回false時,<code>dispatcherservlet</code> 假定攔截器本身已經處理了請求(并且例如呈現适當的視圖),并且不會繼續執行其他攔截器和執行鍊中的實際處理程式。

攔截器可以使用<code>interceptors</code>屬性進行配置,該屬性存在于所有<code>handlermapping</code>類中<code>abstracthandlermapping</code>。這在下面的示例中顯示:

該映射處理的任何請求都被截取<code>timebasedaccessinterceptor</code>。如果目前時間在辦公時間之外,使用者将被重定向到靜态html檔案,例如,您隻能在辦公時間内通路該網站。

《Spring 5 官方文檔》18. Web MVC 架構(六)

當使用<code>requestmappinghandlermapping</code>實際處理程式時,<code>handlermethod</code>它的一個執行個體 辨別将被調用的特定控制器方法。

您可以看到,spring擴充卡類<code>handlerinterceptoradapter</code>可以更容易地擴充<code>handlerinterceptor</code>接口。

《Spring 5 官方文檔》18. Web MVC 架構(六)

請注意,該<code>posthandle</code>方法<code>handlerinterceptor</code>并不總是非常适用于<code>@responsebody</code>和<code>responseentity</code>方法。在這種情況下<code>httpmessageconverter</code> ,在<code>posthandle</code>調用之前寫入并送出響應,這使得不可能更改響應,例如添加标題。相反,應用程式可以實作 <code>responsebodyadvice</code>并将其聲明為<code>@controlleradvice</code>bean或直接配置它<code>requestmappinghandleradapter</code>。

對于spring處理視圖的方式來說重要的兩個接口是<code>viewresolver</code> 和<code>view</code>。所述<code>viewresolver</code>提供視圖名稱和實際視圖之間的映射。該<code>view</code>接口解決了請求的準備,并将請求交給一種視圖技術。

<a href="http://docs.spring.io/spring/docs/5.0.0.m5/spring-framework-reference/html/mvc.html#mvc-view-resolvers-tbl"></a>

<b>表18.3。檢視解析器</b>

視圖解析器

描述

<code>abstractcachingviewresolver</code>

抽象視圖解析器緩存視圖。通常情況下,需要準備才能使用; 擴充此視圖解析器提供緩存。

<code>xmlviewresolver</code>

實作<code>viewresolver</code>它接受使用與spring的xml bean工廠相同的dtd使用xml編寫的配置檔案。預設配置檔案是<code>/web-inf/views.xml</code>。

<code>resourcebundleviewresolver</code>

實作<code>viewresolver</code>它使用bean定義<code>resourcebundle</code>,由bundle基本名稱指定。通常,您可以在屬性檔案中定義bundle,該屬性檔案位于類路徑中。預設檔案名是<code>views.properties</code>。

<code>urlbasedviewresolver</code>

簡單地實作<code>viewresolver</code>了直接解析邏輯視圖名稱到url的接口,而沒有明确的映射定義。如果您的邏輯名稱以直覺的方式與視圖資源的名稱比對,則這是适當的,而不需要任意映射。

<code>internalresourceviewresolver</code>

友善的子類<code>urlbasedviewresolver</code>支援<code>internalresourceview</code>(實際上是servlet和jsp)和子類,如<code>jstlview</code>和<code>tilesview</code>。您可以通過使用為此解析器生成的所有視圖指定視圖類 <code>setviewclass(..)</code>。有關<code>urlbasedviewresolver</code>詳細資訊,請參閱javadoc。

<code>freemarkerviewresolver</code>

它的便利子類<code>urlbasedviewresolver</code>支援<code>freemarkerview</code>和自定義子類。

<code>contentnegotiatingviewresolver</code>

例如,使用jsp作為視圖技術,可以使用<code>urlbasedviewresolver</code>。此視圖解析器将視圖名稱轉換為url,并将請求轉交給requestdispatcher以呈現視圖。

當<code>test</code>以邏輯視圖名稱傳回時,此視圖解析器将請求轉發到<code>requestdispatcher</code>将要發送的請求<code>/web-inf/jsp/test.jsp</code>。

當您在web應用程式中組合不同的視圖技術時,可以使用 <code>resourcebundleviewresolver</code>:

在<code>resourcebundleviewresolver</code>考察<code>resourcebundle</code>确定了基本名字和它應該解決每個視圖,它使用屬性的值 <code>[viewname].(class)</code>作為視圖類和屬性的值<code>[viewname].url</code>作為視圖的url。示例可以在下一章中找到,涵蓋視圖技術。您可以看到,您可以識别父視圖,從屬性檔案中的所有視圖“擴充”。這樣,您可以指定預設視圖類。

《Spring 5 官方文檔》18. Web MVC 架構(六)

<code>abstractcachingviewresolver</code>他們解析的緩存視圖執行個體的子類。緩存提高了某些視圖技術的性能。可以通過将<code>cache</code>屬性設定為關閉緩存<code>false</code>。此外,如果您必須在運作時重新整理某個視圖(例如,當freemarker模闆被修改時),則可以使用該<code>removefromcache(string viewname, locale loc)</code>方法。

spring支援多個視圖解析器。是以,您可以連結解析器,并且在某些情況下例如覆寫特定視圖。您可以通過在應用程式上下文中添加多個解析器來連結視圖解析器,如有必要,可以通過設定 <code>order</code>屬性來指定排序。記住,order屬性越高,視圖解析器在鍊中的位置越晚。

在以下示例中,視圖解析器由兩個解析器組成,一個 <code>internalresourceviewresolver</code>始終自動定位為鍊中的最後一個解析器,另一個<code>xmlviewresolver</code>用于指定excel視圖。excel不支援excel視圖<code>internalresourceviewresolver</code>。

如果一個特定的視圖解析器不會産生視圖,spring會檢查其他視圖解析器的上下文。如果存在另外的視圖解析器,spring會繼續檢查它們,直到視圖解決。如果沒有視圖解析器傳回一個視圖,spring會抛出一個 <code>servletexception</code>。

視圖解析器的合同指定視圖解析器可以傳回null以訓示無法找到視圖。然而,并不是所有的視圖解析器都這樣做,因為在某些情況下,解析器根本無法檢測視圖是否存在。例如,内部<code>internalresourceviewresolver</code>使用<code>requestdispatcher</code>,分派是确定jsp是否存在的唯一方法,但此操作隻能執行一次。對于<code>freemarkerviewresolver</code>其他一些人也是如此。檢查特定視圖解析器的javadoc以檢視是否報告不存在的視圖。是以,把一個<code>internalresourceviewresolver</code>在鍊中比在鍊中的最後結果的其他地方沒有得到充分檢驗,因為 <code>internalresourceviewresolver</code>意志總是傳回一個視圖!

如前所述,控制器通常傳回邏輯視圖名稱,視圖解析器解析為特定視圖技術。對于視圖技術如jsp,其通過servlet或jsp引擎處理,此分辨率通常是通過組合處理<code>internalresourceviewresolver</code>和 <code>internalresourceview</code>,它發出一個内部正向或包括經由在servlet api的<code>requestdispatcher.forward(..)</code>方法或<code>requestdispatcher.include()</code>方法。對于其他視圖技術,如freemarker,xslt等,視圖本身将内容直接寫入響應流。

在呈現視圖之前,有時需要将http重定向發回用戶端。這是可取的,例如,當一個控制器已經被調用了 <code>post</code>資料時,并且響應實際上是對另一個控制器的委派(例如,成功的表單送出)。在這種情況下,正常的内部向前将意味着另一個控制器也将看到相同的<code>post</code>資料,如果它可能與其他預期資料混淆,這是潛在的問題。在顯示結果之前執行重定向的另一個原因是消除使用者多次送出表單資料的可能性。在這種情況下,浏覽器将首先發送一個初始的<code>post</code>; 然後會收到重定向到其他url的響應; <code>get</code>最後浏覽器将為重定向響應中指定的url執行後續操作。是以,從浏覽器的角度來看,目前頁面并不反映的結果<code>post</code>,而是一個<code>get</code>。最終的效果是使用者無法<code>post</code>通過執行重新整理來意外重新獲得相同的資料。重新整理強制<code>get</code>結果頁面a,而不是重新發送初始<code>post</code>資料。

作為控制器響應的結果強制重定向的一種方法是控制器建立并傳回spring的執行個體<code>redirectview</code>。在這種情況下, <code>dispatcherservlet</code>不使用普通視圖分辨機制。而是因為已經給了(重定向)視圖,<code>dispatcherservlet</code>簡單地訓示視圖來完成它的工作。将<code>redirectview</code>依次調用<code>httpservletresponse.sendredirect()</code> 發送一個http重定向到用戶端浏覽器。

預設情況下,所有模型屬性都被認為是重定向url中的uri模闆變量。在剩餘的屬性中,原始類型或原始類型的集合/數組的那些屬性将自動附加為查詢參數。

如果為重定向準備了模型執行個體,則将原始類型屬性作為查詢參數附加可能是所需的結果。然而,在注釋控制器中,模型可能包含為渲染目的添加的附加屬性(例如下拉字段值)。為了避免這種屬性出現在url中的可能性,一種<code>@requestmapping</code>方法可以聲明一個類型的參數,<code>redirectattributes</code>并使用它來指定可供使用的确切屬性<code>redirectview</code>。如果方法重定向,則使用内容<code>redirectattributes</code>。否則使用模型的内容。

在<code>requestmappinghandleradapter</code>提供了一個名為标志 <code>"ignoredefaultmodelonredirect"</code>,可以用來表示預設的内容 <code>model</code>,如果一個控制器方法重定向不應該被使用。相反,控制器方法應該聲明一個類型的屬性,<code>redirectattributes</code>或者如果它不這樣做,則不應該傳遞任何屬性<code>redirectview</code>。mvc命名空間和mvc java配置都将此标志設定<code>false</code>為保持向後相容性。但是,對于新的應用程式,我們建議将其設定為<code>true</code>

請注意,當擴充重定向網址時,來自本請求的uri模闆變量将自動提供,并且不需要通過<code>model</code>或不顯式添加<code>redirectattributes</code>。例如:

雖然使用<code>redirectview</code>工程正常,如果控制器本身建立 <code>redirectview</code>,則沒有避免控制器知道重定向發生的事實。這是非常不合時宜的事情,太緊密地結合在一起。控制器不應該真正關心響應如何處理。一般來說,它應該僅在注入到其中的視圖名稱的方式操作。

特殊的<code>redirect:</code>字首允許你完成這個。如果傳回具有字首的視圖名稱<code>redirect:</code>,則<code>urlbasedviewresolver</code>(和所有子類)将會将其識别為需要重定向的特殊訓示。視圖名稱的其餘部分将被視為重定向網址。

淨效果與控制器傳回一樣<code>redirectview</code>,但現在控制器本身可以簡單地按邏輯視圖名稱進行操作。一個邏輯視圖名稱,例如<code>redirect:/myapp/some/resource</code>将重定向到目前的servlet上下文,而一個名稱<code>redirect:http://myhost.com/some/arbitrary/path</code> 将重定向到絕對url。

請注意,控制器處理程式使用注釋<code>@responsestatus</code>,注釋值優先于設定的響應狀态<code>redirectview</code>。

也可以使用<code>forward:</code>最終由子類決定的視圖名稱的特殊字首<code>urlbasedviewresolver</code>。這将建立一個 視圖名稱<code>internalresourceview</code>(其最終将<code>requestdispatcher.forward()</code>圍繞其被視為url)的視圖名稱。是以,這個字首對于<code>internalresourceviewresolver</code>和<code>internalresourceview</code>(對于jsp而言)不是有用的。但是,當您主要使用另一種視圖技術時,字首可能會有所幫助,但是仍然希望強制轉發由servlet / jsp引擎處理的資源。(請注意,您也可以連結多個視圖解析器。)

與<code>redirect:</code>字首一樣,如果具有字首的視圖名稱<code>forward:</code>注入到控制器中,則控制器在處理響應方面沒有檢測到發生任何特殊事件。