Spring - Java/J2EE Application Framework 应用框架 第 12 章 Web框架
第?12?章?Web框架
12.1.?Web框架介紹
Spring的web框架是圍繞分發器(DispatcherServlet)設計的,DispatcherServlet將請求分發到不同的處理器,框架還包括可配置的處理器映射,視圖解析,本地化,主題解析,還支持文件上傳。缺省的處理器是一個簡單的控制器(Controller)接口,這個接口僅僅定義了ModelAndView handleRequest(request,response)方法。你可以實現這個接口生成應用的控制器,但是使用Spring提供的一系列控制器實現會更好一些,比如AbstractController,AbstractCommandController,和SimpleFormController。應用控制器一般都從它們繼承。注意你需要選擇正確的基類:如果你沒有表單,你就不需要一個FormController。這是和Structs的一個主要區別。
你可以使用任何對象作為命令對象或表單對象:不必實現某個接口或從某個基類繼承。Spring的數據綁定相當靈活,例如,它認為類型不匹配這樣的錯誤應該是應用級的驗證錯誤,而不是系統錯誤。所以你不需要為了處理無效的表單提交,或者正確地轉換字符串,在你的表單對象中用字符串類型重復定義你的業務對象屬性。你應該直接綁定表單到業務對象上。這是和Struts的另一個重要不同,Struts是圍繞象Action和ActionForm這樣的基類構建的,每一種行為都是它們的子類。
和WebWork相比,Spring將對象細分成不同的角色:它支持的概念有控制器(Controller),可選的命令對象(Command Object)或表單對象(Form Object),以及傳遞到視圖的模型(Model)。模型不僅包含命令對象或表單對象,而且也包含任何引用數據。但是,WebWork的Action將所有的這些角色都合并在一個單獨的對象里。WebWork允許你在表單中使用現有的業務對象,但是只能把它們定義成不同Action類的bean屬性。更重要的是,在運算和表單賦值時,使用的是同一個處理請求的Action實例。因此,引用數據也需要被定義成Action的bean屬性。這樣在一個對象就承擔了太多的角色。
對于視圖:Spring的視圖解析相當靈活。一個控制器實現甚至可以直接輸出一個視圖作為響應,這需要使用null返回ModelAndView。在一般的情況下,一個ModelAndView實例包含視圖名字和模型映射表,模型映射表提供了bean的名字及其對象(比如命令對象或表單對象,引用數據等等)的對應關系。視圖名解析的配置是非常靈活的,可以通過bean的名字,屬性文件或者你自己的ViewResolver來實現。抽象的模型映射表完全抽象了表現層,沒有任何限制:JSP,Velocity,或者其它的技術——任何表現層都可以直接和Spring集成。模型映射表僅僅將數據轉換成合適的格式,比如JSP請求屬性或者Velocity模版模型。
12.1.1.?MVC實現的可擴展性
許多團隊努力爭取在技術和工具方面能使他們的投入更有價值,無論是現有的項目還是新的項目都是這樣。具體地說,Struts 不僅有大量的書籍和工具,而且有許多開發者熟悉它。因此,如果你能忍受Struts的架構性缺陷,它仍然是web層一個很好的選擇。WebWork和其它web框架也是這樣。
如果你不想使用Spring的web MVC框架,而僅僅想使用Spring提供的其它功能,你可以很容易地將你選擇的web框架和Spring結合起來。只要通過Spring的ContextLoadListener啟動一個Spring的根應用上下文,并且通過它的ServletContext屬性(或者Spring的各種幫助方法)在Struts或WebWork的Action中訪問。注意到現在沒有提到任何具體的“plugins”,因此這里也沒有提及如何集成:從web層的角度看,你可以僅僅把Spring作為一個庫使用,根應用上下文實例作為入口。
所有你注冊的bean和Spring的服務可以在沒有Spring的web MVC下被訪問。Spring并沒有在使用方法上和Struts或WebWork競爭,它只是提供單一web框架所沒有的功能,從bean的配置到數據訪問和事務處理。所以你可以使用Spring的中間層和(或者)數據訪問層來增強你的應用,即使你只是使用象JDBC或Hibernate事務抽象這樣的功能。
12.1.2.?Spring MVC框架的特點
如果僅僅關注于web方面的支持,Spring有下面一些特點:
-
清晰的角色劃分:控制器,驗證器,命令對象,表單對象和模型對象;分發器,處理器映射和視圖解析器;等等。
-
直接將框架類和應用類都作為JavaBean配置,包括通過應用上下文配置中間層引用,例如,從web控制器到業務對象和驗證器的引用。
-
可適應性,但不具有強制性:根據不同的情況,使用任何你需要的控制器子類(普通控制器,命令,表單,向導,多個行為,或者自定義的),而不是要求任何東西都要從Action/ActionForm繼承。
-
可重用的業務代碼,而不需要代碼重復:你可以使用現有的業務對象作為命令對象或表單對象,而不需要在ActionForm的子類中重復它們的定義。
-
可定制的綁定和驗證:將類型不匹配作為應用級的驗證錯誤,這可以保存錯誤的值,以及本地化的日期和數字綁定等,而不是只能使用字符串表單對象,手動解析它并轉換到業務對象。
-
可定制的處理器映射,可定制的視圖解析:靈活的模型可以根據名字/值映射,處理器映射和視圖解析使應用策略從簡單過渡到復雜,而不是只有一種單一的方法。
-
可定制的本地化和主題解析,支持JSP,無論有沒有使用Spring標簽庫,支持JSTL,支持不需要額外過渡的Velocity,等等。
-
簡單而強大的標簽庫,它盡可能地避免在HTML生成時的開銷,提供在標記方面的最大靈活性。
12.2.?分發器(DispatcherServlet)
Spring的web框架——象其它web框架一樣——是一個請求驅動的web框架,其設計圍繞一個能將請求分發到控制器的servlet,它也提供其它功能幫助web應用開發。然而,Spring的DispatcherServlet所做的不僅僅是這些。它和Spring的ApplicationContext完全集成在一起,允許你使用Spring的其它功能。
DispatcherServlet和其它servlet一樣定義在你的web應用的web.xml文件里。DispatcherServlet處理的請求必須在同一個web.xml文件里使用url-mapping定義映射。
<web-app>...<servlet><servlet-name>example</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>example</servlet-name><url-pattern>*.form</url-pattern></servlet-mapping> </web-app>在上面的例子里,所有以.form結尾的請求都會由DispatcherServlet處理。接下來需要配置DispatcherServlet本身。正如在第?3.10?節 “介紹ApplicationContext”中所描述的,Spring中的ApplicationContexts可以被限制在不同的作用域。在web框架中,每個DispatcherServlet有它自己的WebApplicationContext,它包含了DispatcherServlet配置所需要的bean。DispatcherServlet 使用的缺省BeanFactory是XmlBeanFactory,并且DispatcherServlet在初始化時會在你的web應用的WEB-INF目錄下尋找[servlet-name]-servlet.xml文件。DispatcherServlet使用的缺省值可以使用servlet初始化參數修改(詳細信息如下)。
WebApplicationContext僅僅是一個擁有web應用必要功能的普通ApplicationContext。它和一個標準的ApplicationContext的不同之處在于它能夠解析主題(參考第?12.7?節 “主題使用”),并且它知道和那個servlet關聯(通過到ServletContext的連接)。WebApplicationContext被綁定在ServletContext上,當你需要的時候,可以使用RequestContextUtils找到WebApplicationContext。
Spring的DispatcherServlet有一組特殊的bean,用來處理請求和顯示相應的視圖。這些bean包含在Spring的框架里,(可選擇)可以在WebApplicationContext中配置,配置方式就象配置其它bean的方式一樣。這些bean中的每一個都在下面被詳細描述。待一會兒,我們就會提到它們,但這里僅僅是讓你知道它們的存在以便我們繼續討論DispatcherServlet。對大多數bean,都提供了缺省值,所有你不必要擔心它們的值。
表?12.1.?WebApplicationContext中特殊的bean
| 處理器映射(handler mapping(s)) | (第?12.4?節 “處理器映射”) 前處理器,后處理器和控制器的列表,它們在符合某種條件下才被執行(例如符合控制器指定的URL) |
| 控制器(controller(s)) | (第?12.3?節 “控制器”) 作為MVC三層一部分,提供具體功能(或者至少能夠訪問具體功能)的bean |
| 視圖解析器(view resolver) | (第?12.5?節 “視圖與視圖解析”) 能夠解析視圖名,在DispatcherServlet解析視圖時使用 |
| 本地化信息解析器(locale resolver) | (第?12.6?節 “使用本地化信息”) 能夠解析用戶正在使用的本地化信息,以提供國際化視圖 |
| 主題解析器(theme resolver) | (第?12.7?節 “主題使用”) 能夠解析你的web應用所使用的主題,比如,提供個性化的布局 |
| multipart解析器 | (第?12.8?節 “Spring對multipart(文件上傳)的支持”) 提供HTML表單文件上傳功能 |
| 處理器異常解析器(handlerexception resolver) | (第?12.9?節 “處理異?!? 將異常對應到視圖,或者實現某種復雜的異常處理代碼 |
當DispatcherServlet被安裝配置好,DispatcherServlet一接收到請求,處理就開始了。下面的列表描述了DispatcherServlet處理請求的全過程:
搜索WebApplicationContext,并將它綁定到請求的一個屬性上,以便控制器和處理鏈上的其它處理器能使用WebApplicationContext。缺省它被綁定在DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE這個關鍵字上
綁定本地化信息解析器到請求上,這樣使得處理鏈上的處理器在處理請求(顯示視圖,準備數據等等)時能解析本地化信息。如果你不使用本地化信息解析器,它不會影響任何東西,忽略它就可以了
綁定主題解析器到請求上,使得視圖決定使用哪個主題(如果你不需要主題,可以忽略它,解析器僅僅是綁定,如果你不使用它,不會影響任何東西)
如果multipart解析器被指定,請求會被檢查是否使用了multipart,如果是,multipart解析器會被保存在MultipartHttpServletRequest中以便被處理鏈中的其它處理器使用(下面會講到更多有關multipart處理的內容)
搜索合適的處理器。如果找到,執行和這個處理器相關的執行鏈(預處理器,后處理器,控制器),以便準備模型數據
如果模型數據被返還,就使用配置在WebApplicationContext中的視圖解析器,顯示視圖,否則(可能是安全原因,預處理器或后處理器截取了請求),雖然請求能夠提供必要的信息,但是視圖也不會被顯示。
在請求處理過程中拋出的異??梢员蝗魏味x在WebApplicationContext中的異常解析器所獲取。使用這些異常解析器,你可以在異常拋出時定義特定行為。
Spring的DispatcherServlet也支持返回Servlet API定義的last-modification-date,決定某個請求最后修改的日期很簡單。DispatcherServlet會首先尋找一個合適的處理器映射,檢查處理器是否實現了LastModified接口,如果是,將long getLastModified(request)的值返回給客戶端。
你可以在web.xml文件中添加上下文參數或servlet初始化參數定制Spring的DispatcherServlet。下面是一些可能的參數。
表?12.2.?DispatcherServlet初始化參數
| contextClass | 實現WebApplicationContext的類,當前的servlet用它來實例化上下文。如果這個參數沒有指定,使用XmlWebApplicationContext |
| contextConfigLocation | 傳給上下文實例(由contextClass指定)的字符串,用來指定上下文的位置。這個字符串可以被分成多個字符串(使用逗號作為分隔符)來支持多個上下文(在多上下文的情況下,被定義兩次的bean中,后面一個優先) |
| namespace | WebApplicationContext命名空間。缺省是[server-name]-servlet |
12.3.?控制器
控制器的概念是MVC設計模式的一部分??刂破鞫x了應用的行為,至少能使用戶訪問到這些行為??刂破鹘忉層脩糨斎?#xff0c;并將其轉換成合理的模型數據,從而可以進一步地由視圖展示給用戶。Spring以一種抽象的方式實現了控制器概念,這樣使得不同類型的控制器可以被創建。Spring包含表單控制器,命令控制器,執行向導邏輯的控制器等等。
Spring控制器架構的基礎是org.springframework.mvc.Controller接口。
public interface Controller {/*** Process the request and return a ModelAndView object which the DispatcherServlet* will render.*/ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception; }你可以發現Controller接口僅僅聲明了一個方法,它能夠處理請求并返回合適的模型和視圖。Spring MVC實現的基礎就是這三個概念:ModelAndView和Controller。 因為Controller接口是完全抽象的,Spring提供了許多已經包含一定功能的控制器??刂破鹘涌趦H僅定義了每個控制器提供的共同功能:處理請求并返回一個模型和一個視圖。
12.3.1.?AbstractController 和 WebContentGenerator
當然,就一個控制器接口并不夠。為了提供一套基礎設施,所有的Spring控制器都從 AbstractController 繼承,AbstractController 提供緩存和其它比如 mimetype 的設置的功能。
表?12.3.?AbstractController提供的功能
| supportedMethods | 指定這個控制器應該接受什么樣的請求方法。通常它被設置成GET和POST,但是你可以選擇你想支持的方法。如果控制器不支持請求發送的方法,客戶端會得到通知(ServletException) |
| requiresSession | 指定這個控制器是否需要會話。這個功能提供給所有控制器。如果控制器在沒有會話的情況下接收到請求,用戶被通知ServletException |
| synchronizeSession | 如果你需要使控制器同步訪問用戶會話,使用這個參數。具體地說,繼承的控制器要重載handleRequestInternal方法,如果你指定了這個變量,控制器就被同步化。 |
| cacheSeconds | 當你需要控制器在HTTP響應中生成緩存指令,用這參數指定一個大于零的整數。缺省它被設置為-1,所以就沒有生成緩存指令 |
| useExpiresHeader | 指定你的控制器使用HTTP 1.0兼容的"Expires"。缺省為true,所以你可以不用修改它 |
| useCacheHeader | 指定你的控制器使用HTTP 1.0兼容的"Cache-Control"。缺省為true,所以你也可以不用修改它 |
最后的兩個屬性是WebContentGenerator定義的,WebContentGenerator是AbstractController的超類……
當使用AbstractController作為你的控制器基類時(一般不推薦這樣做,因為有許多預定義的控制器你可以選擇),你只需要重載handleRequestInternal(HttpServletRequest, HttpServletResponse)這個方法,實現你自己的邏輯,并返回一個ModelAndView對象。下面這個簡單例子包含一個類和在web應用上下文中的定義。
package samples;public class SampleController extends AbstractController {public ModelAndView handleRequestInternal(HttpServletRequest request,HttpServletResponse response)throws Exception {ModelAndView mav = new ModelAndView("foo", new HashMap());} } <bean id="sampleController" class="samples.SampleController"><property name="cacheSeconds"><value>120</value</property> </bean>除了這個類和在web應用上下文中的定義,還需要設置處理器映射(參考第?12.4?節 “處理器映射”),這樣這個簡單的控制器就可以工作了。這個控制器將生成緩存指令告訴客戶端緩存數據2分鐘后再檢查狀態。這個控制器還返回了一個硬編碼的視圖名(不是很好)(詳情參考第?12.5?節 “視圖與視圖解析”)。
12.3.2.?其它的簡單控制器
除了AbstractController——雖然有許多其他控制器可以提供給你更多的功能,但是你還是可以直接繼承AbstractController——有許多簡單控制器,它們可以減輕開發簡單MVC應用時的負擔。ParameterizableViewController基本上和上面例子中的一樣,但是你可以指定返回的視圖名,視圖名定義在web應用上下文中(不需要硬編碼的視圖名)
FileNameViewController檢查URL并獲取文件請求的文件名(http://www.springframework.org/index.html的文件名是index),把它作為視圖名。僅此而已。
12.3.3.?MultiActionController
Spring提供一個多動作控制器,使用它你可以將幾個動作合并在一個控制器里,這樣可以把功能組合在一起。多動作控制器存在在一個單獨的包中——org.springframework.web.mvc.multiaction——它能夠將請求映射到方法名,然后調用正確的方法。比如當你在一個控制器中有很多公共的功能,但是想多個入口到控制器使用不同的行為,使用多動作控制器就特別方便。
表?12.4.?MultiActionController提供的功能
| delegate | MultiActionController有兩種使用方式。第一種是繼承MultiActionController,并在子類中指定由MethodNameResolver解析的方法(這種情況下不需要這個配置參數),第二種是你定義了一個代理對象,由它調用Resolver解析的方法。如果你是這種情況,你必須使用這個配置參數定義代理對象 |
| methodNameResolver | 由于某種原因,MultiActionController需要基于收到的請求解析它必須調用的方法。你可以使用這個配置參數定義一個解析器 |
一個多動作控制器的方法需要符合下列格式:
// actionName can be replaced by any methodname ModelAndView actionName(HttpServletRequest, HttpServletResponse);由于MultiActionController不能判斷方法重載(overloading),所以方法重載是不允許的。此外,你可以定義exception handlers,它能夠處理從你指定的方法中拋出的異常。包含異常處理的動作方法需要返回一個ModelAndView對象,就象其它動作方法一樣,并符合下面的格式:
// anyMeaningfulName can be replaced by any methodname ModelAndView anyMeaningfulName(HttpServletRequest, HttpServletResponse, ExceptionClass);ExceptionClass可以是任何異常,只要它是java.lang.Exception或java.lang.RuntimeException的子類。
MethodNameResolver根據收到的請求解析方法名。有三種解析器可以供你選擇,當然你可以自己實現解析器。
-
ParameterMethodNameResolver?- 解析請求參數,并將它作為方法名(http://www.sf.net/index.view?testParam=testIt的請求就會調用testIt(HttpServletRequest,HttpServletResponse))。使用paramName配置參數可以調整所檢查的參數
-
InternalPathMethodNameResolver?- 從路徑中獲取文件名作為方法名(http://www.sf.net/testing.view的請求會調用testing(HttpServletRequest, HttpServletResponse)方法)
-
PropertiesMethodNameResolver?- 使用用戶定義的屬性對象將請求的URL映射到方法名。當屬性定義/index/welcome.html=doIt,并且收到/index/welcome.html的請求,就調用doIt(HttpServletRequest, HttpServletResponse)方法。這個方法名解析器需要使用PathMatcher(參考?第?12.10.1?節 “關于pathmatcher的小故事”)所以如果屬性包含/**/welcom?.html,該方法也會被調用!
我們來看一組例子。首先是一個使用ParameterMethodNameResolver和代理屬性的例子,它接受包含參數名的請求,調用方法retrieveIndex:
<bean id="paramResolver" class="org....mvc.multiaction.ParameterMethodNameResolver"><property name="paramName"><value>method</value></property> </bean><bean id="paramMultiController" class="org....mvc.multiaction.MultiActionController"><property name="methodNameResolver"><ref bean="paramResolver"/></property><property name="delegate"><ref bean="sampleDelegate"/> </bean><bean id="sampleDelegate" class="samples.SampleDelegate"/>## together withpublic class SampleDelegate {public ModelAndView retrieveIndex(HttpServletRequest req, HttpServletResponse resp) {rerurn new ModelAndView("index", "date", new Long(System.currentTimeMillis()));} }當使用上面的代理對象時,我們也可以使用PropertiesMethodNameRseolver來匹配一組URL,將它們映射到我們定義的方法上:
<bean id="propsResolver" class="org....mvc.multiaction.PropertiesMethodNameResolver"><property name="mappings"><props><prop key="/index/welcome.html">retrieveIndex</prop><prop key="/**/notwelcome.html">retrieveIndex</prop><prop key="/*/user?.html">retrieveIndex</prop></props></property> </bean><bean id="paramMultiController" class="org....mvc.multiaction.MultiActionController"><property name="methodNameResolver"><ref bean="propsResolver"/></property><property name="delegate"><ref bean="sampleDelegate"/> </bean>12.3.4.?命令控制器
Spring的CommandControllers是Spring MVC包的重要部分。命令控制器提供了一種和數據對象交互的方式,并動態將來自HttpServletRequest的參數綁定到你指定的數據對象上。和Struts的actonform相比,在Spring中,你不需要實現任何接口來實現數據綁定。首先,讓我們看一下有哪些可以使用的命令控制器,以便有一個清晰的了解:
-
AbstractCommandController?- 你可以使用這個命令控制器來創建你自己的命令控制器,它能夠將請求參數綁定到你指定的數據對象。這個類并不提供任何表單功能,但是它提供驗證功能,并且讓你在控制器中定義如何處理包含請求參數的數據對象。
-
AbstractFormController?- 一個提供表單提交支持的控制器。使用這個控制器,你可以定義表單,并使用你從控制器獲取的數據對象構建表單。當用戶輸入表單內容,AbstractFormController將用戶輸入的內容綁定到數據對象,驗證這些內容,并將對象交給控制器,完成適當的動作。它所支持的功能有無效表單提交(再次提交),驗證,和正確的表單工作流。你可以控制將什么視圖綁定到你的AbstractFormController。如果你需要表單,但不想在應用上下文中指定顯示給用戶的視圖,就使用這個控制器。
-
SimpleFormController?- 這是一個更具體的FormCotnroller,它能用相應的數據對象幫助你創建表單。SimpleFormController讓你指定一個命令對象,表單視圖名,當表單提交成功后顯示給用戶的視圖名等等。
-
WizardFormController?- 最后一個也是功能最強的控制器。WizardFormController 允許你以向導風格處理數據對象,當使用大的數據對象時,這樣的方式相當方便。
12.4.?處理器映射
使用處理器映射,你可以將web請求映射到正確的處理器上。有很多處理器映射你可以使用,例如:SimpleUrlHandlerMapping或者BeanNameUrlHandlerMapping,但是先讓我們看一下HandlerMapping的基本概念。
一個基本的HandlerMapping所提供的功能是將請求傳遞到HandlerExecutionChain上,首先HandlerExecutionChain包含一個符合輸入請求的處理器。其次(但是可選的)是一個可以攔截請求的攔截器列表。當收到請求,DispatcherServlet將請求交給處理器映射,讓它檢查請求并獲得一個正確的HandlerExecutionChain。然后,執行定義在執行鏈中的處理器和攔截器(如果有攔截器的話)
包含攔截器(處理器執行前,執行后,或者執行前后)的可配置的處理器映射功能非常強大。許多功能被放置在自定義的HandlerMappings中。一個自定義的處理器映射不僅根據請求的URL,而且還可以根據和請求相關的會話狀態來選擇處理器。
我們來看看Spring提供的處理器映射。
12.4.1.?BeanNameUrlHandlerMapping
BeanNameUrlHandlerMapping是一個簡單但很強大的處理器映射,它將收到的HTTP請求映射到在web應用上下文中定義的bean的名字上。如果我們想要使用戶插入一個賬戶,并且假設我們提供了FormController(關于CommandController和FormController請參考第?12.3.4?節 “命令控制器”)和顯示表單的JSP視圖(或Velocity模版)。當使用BeanNameUrlHandlerMapping時,我們用下面的配置能將包含URL?http://samples.com/editaccount.form的HTTP請求映射到合適的FormController上:
<beans><bean id="handlerMapping" class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/><bean name="/editaccount.form"class="org.springframework.web.servlet.mvc.SimpleFormController"><property name="formView"><value>account</value></property><property name="successView"><value>account-created</value></property><property name="commandName"><value>Account</value></property><property name="commandClass"><value>samples.Account</value></property></bean> <beans>所有/editaccount.form的請求就會由上面的FormController處理。當然我們得在web.xml中定義servlet-mapping,接受所有以.form結尾的請求。
<web-app>...<servlet><servlet-name>sample</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><load-on-startup>1</load-on-startup></servlet><!-- Maps the sample dispatcher to /*.form --><servlet-mapping><servlet-name>sample</servlet-name><url-pattern>*.form</url-pattern></servlet-mapping>... </web-app>注意:如果你使用BeanNameUrlHandlerMapping,你不必在web應用上下文中定義它。缺省情況下,如果在上下文中沒有找到處理器映射,DispatcherServlet會為你創建一個BeanNameUrlHandlerMapping!
12.4.2.?SimpleUrlHandlerMapping
另一個——更強大的處理器映射——是SimpleUrlHandlerMapping。它在應用上下文中可以配置,并且有Ant風格的路徑匹配功能(參考第?12.10.1?節 “關于pathmatcher的小故事”)。下面幾個例子可以幫助理解:
<web-app>...<servlet><servlet-name>sample</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><load-on-startup>1</load-on-startup></servlet><!-- Maps the sample dispatcher to /*.form --><servlet-mapping><servlet-name>sample</servlet-name><url-pattern>*.form</url-pattern></servlet-mapping><servlet-mapping><servlet-name>sample</servlet-name><url-pattern>*.html</url-pattern></servlet-mapping>... </web-app>允許所有以.html和.form結尾的請求都由這個示例dispatchservelt處理。
<beans><bean id="handlerMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="mappings"><props><prop key="/*/account.form">editAccountFormController</prop><prop key="/*/editaccount.form">editAccountFormController</prop><prop key="/ex/view*.html">someViewController</prop><prop key="/**/help.html">helpController</prop></props></property></bean><bean id="someViewController"class="org.springframework.web.servlet.mvc.FilenameViewController"/><bean id="editAccountFormController"class="org.springframework.web.servlet.mvc.SimpleFormController"><property name="formView"><value>account</value></property><property name="successView"><value>account-created</value></property><property name="commandName"><value>Account</value></property><property name="commandClass"><value>samples.Account</value></property></bean> <beans>這個處理器映射首先將所有目錄中文件名為help.html的請求傳遞給helpController(譯注,原文為someViewController),someViewController是一個FilenameViewController(更多信息請參考第?12.3?節 “控制器”)。所有ex目錄中資源名以view開始,.html結尾的請求都會被傳遞給控制器。這里定義了兩個使用editAccountFormController的處理器映射。
12.4.3.?添加HandlerInterceptors
處理器映射提供了攔截器概念,當你想要為所有請求提供某種功能時,例如做某種檢查,這就非常有用。
處理器映射中的攔截器必須實現org.springframework.web.servlet包中的HandlerInterceptor接口。這個接口定義了三個方法,一個在處理器執行前被調用,一個在處理器執行后被調用,另一個在整個請求處理完后調用。這三個方法提供你足夠的靈活度做任何處理前和處理后的操作。
preHandle方法有一個boolean返回值。使用這個值,你可以調整執行鏈的行為。當返回true時,處理器執行鏈將繼續執行,當返回false時,DispatcherServlet認為攔截器本身將處理請求(比如顯示正確的視圖),而不繼續執行執行鏈中的其它攔截器和處理器。
下面的例子提供了一個攔截器,它攔截所有請求,如果當前時間是在上午9點到下午6點,將重定向到某個頁面。
<beans><bean id="handlerMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="interceptors"><list><ref bean="officeHoursInterceptor"/></list></property><property name="mappings"><props><prop key="/*.form">editAccountFormController</prop><prop key="/*.view">editAccountFormController</prop></props></property></bean><bean id="officeHoursInterceptor" class="samples.TimeBasedAccessInterceptor"><property name="openingTime"><value>9</value></property><property name="closingTime"><value>18</value></property></bean> <beans> package samples;public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter {private int openingTime; private int closingTime; public void setOpeningTime(int openingTime) {this.openingTime = openingTime;} public void setClosingTime(int closingTime) {this.closingTime = closingTime;} public boolean preHandle(HttpServletRequest request,HttpServletResponse response,Object handler)throws Exception {Calendar cal = Calendar.getInstance();int hour = cal.get(HOUR_OF_DAY);if (openingTime <= hour < closingTime) {return true;} else {response.sendRedirect("http://host.com/outsideOfficeHours.html");return false;}} }任何收到的請求,都將被TimeBasedAccessInterceptor截獲,如果當前時間不在上班時間,用戶會被重定向到一個靜態html頁面,比如告訴他只能在上班時間才能訪問網站。
你可以發現,Spring提供了adapter,使你很容易地使用HandlerInterceptor。
12.5.?視圖與視圖解析
所有web應用的MVC框架都會有它們處理視圖的方式。Spring提供了視圖解析器,這使得你在瀏覽器顯示模型數據時不需要指定具體的視圖技術。Spring允許你使用Java Server Page,Velocity模版和XSLT視圖。第?13?章?集成表現層詳細說明了如何集成不同的視圖技術。
Spring處理視圖的兩個重要的類是ViewResolver和View。View接口為請求作準備,并將請求傳遞給某個視圖技術。ViewResolver提供了一個視圖名和實際視圖之間的映射。
12.5.1.?ViewResolvers
正如前面所討論的,SpringWeb框架的所有控制器都返回一個ModelAndView實例。Spring中的視圖由視圖名識別,視圖解析器解析。Spring提供了許多視圖解析器。我們將列出其中的一些,和它們的例子。
表?12.5.?視圖解析器
| AbstractCachingViewResolver | 抽象視圖解析器,負責緩存視圖。許多視圖需要在使用前作準備,從它繼承的視圖解析器可以緩存視圖。 |
| ResourceBundleViewResolver | 使用ResourceBundle中的bean定義實現ViewResolver,這個ResourceBundle由bundle的basename指定。這個bundle通常定義在一個位于classpath中的一個屬性文件中 |
| UrlBasedViewResolver | 這個ViewResolver實現允許將符號視圖名直接解析到URL上,而不需要顯式的映射定義。如果你的視圖名直接符合視圖資源的名字而不需要任意的映射,就可以使用這個解析器 |
| InternalResourceViewResolver | UrlBasedViewResolver的子類,它很方便地支持InternalResourceView(也就是Servlet和JSP),以及JstlView和TilesView的子類。由這個解析器生成的視圖的類都可以通過setViewClass指定。詳細參考UrlBasedViewResolver的javadocs |
| VelocityViewResolver | UrlBasedViewResolver的子類,它能方便地支持VelocityView(也就是Velocity模版)以及它的子類 |
例如,當使用JSP時,可以使用UrlBasedViewResolver。這個視圖解析器將視圖名翻譯成URL,并將請求傳遞給RequestDispatcher顯示視圖。
<bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver"><property name="prefix"><value>/WEB-INF/jsp/</value></property><property name="suffix"><value>.jsp</value></property> </bean>當返回test作為視圖名時,這個視圖解析器將請求傳遞給RequestDispatcher,RequestDispatcher將請求再傳遞給/WEB-INF/jsp/test.jsp。
當在一個web應用中混合使用不同的視圖技術時,你可以使用ResourceBundleViewResolver:
<bean id="viewResolver"class="org.springframework.web.servlet.view.ResourceBundleViewResolver"><property name="baseName"><value>views</value></property><property name="defaultParentView"><value>parentView</value></property </bean>12.6.?使用本地化信息
Spring架構的絕大部分都支持國際化,就象Spring的web框架一樣。SpringWeb框架允許你使用客戶端本地化信息自動解析消息。這由LocaleResolver對象完成。
當收到請求時,DispatcherServlet尋找一個本地化信息解析器,如果找到它就使用它設置本地化信息。使用RequestContext.getLocale()方法,你總可以獲取本地化信息供本地化信息解析器使用。
除了自動本地化信息解析,你還可以將一個攔截器放置到處理器映射上(參考第?12.4.3?節 “添加HandlerInterceptors”),以便在某種環境下,比如基于請求中的參數,改變本地化信息。
本地化信息解析器和攔截器都定義在org.springframework.web.servlet.i18n包中,并且在你的應用上下文中配置。你可以選擇使用Spring中的本地化信息解析器。
12.6.1.?AcceptHeaderLocaleResolver
這個本地化信息解析器檢查請求中客戶端瀏覽器發送的accept-language頭。通常這個頭信息包含客戶端操作系統的本地化信息。
12.6.2.?CookieLocaleResolver
這個本地化信息解析器檢查客戶端中的cookie是否本地化信息被指定了。如果指定就使用該本地化信息。使用這個本地化信息解析器的屬性,你可以指定cookie名,以及最大生存期。
<bean id="localeResolver"><property name="cookieName"><value>clientlanguage</value></property><!-- in seconds. If set to -1, the cookie is not persisted (deleted when browser shuts down) --><property name="cookieMaxAge"><value>100000</value></property> </bean>這個例子定義了一個CookieLocaleResolver。
表?12.6.?WebApplicationContext中的特殊bean
| cookieName | classname + LOCALE | cookie名 |
| cookieMaxAge | Integer.MAX_INT | cookie在客戶端存在的最大時間。如果該值是-1,這個cookie一直存在,直到客戶關閉它的瀏覽器 |
| cookiePath | / | 使用這個參數,你可以限制cookie只有你的一部分網站頁面可以訪問。當cookiePath被指定,cookie只能被該目錄以及子目錄的頁面訪問 |
12.6.3.?SessionLocaleResolver
SessionLocaleResolver允許你從用戶請求相關的會話中獲取本地化信息。
12.6.4.?LocaleChangeInterceptor
你可以使用LocaleChangeInterceptor修改本地化信息。這個攔截器需要添加到處理器映射中(參考第?12.4?節 “處理器映射”),并且它會在請求中檢查參數修改本地化信息(它在上下文中的LocaleResolver中調用setLocale())。
<bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"><property name="paramName"><value>siteLanguage</value></property> </bean><bean id="localeResolver"class="org.springframework.web.servlet.i18n.CookieLocaleResolver"/><bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="interceptors"><list><ref local="localeChangeInterceptor"/></list></property><property name="mappings"><props><prop key="/**/*.view">someController</prop></props></property> </bean>所有包含參數siteLanguage的*.view資源的請求都會改變本地化信息。所以http://www.sf.net/home.view?siteLanguage=nl的請求會將網站語言修改為荷蘭語。
12.7.?主題使用
空段落
12.8.?Spring對multipart(文件上傳)的支持
12.8.1.?介紹
Spring由內置的multipart支持web應用中的文件上傳。multipart支持的設計是通過定義org.springframework.web.multipart包中的插件對象MultipartResovler來完成的。Spring提供MultipartResolver可以支持Commons FileUpload?(http://jakarta.apache.org/commons/fileupload)和COS FileUpload?(http://www.servlets.com/cos)。本章后面的部分描述了文件上傳是如何支持的。
缺省,Spring是沒有multipart處理,因為一些開發者想要自己處理它們。如果你想使用Spring的multipart,需要在web應用的上下文中添加multipart解析器。這樣,每個請求就會被檢查是否包含multipart。然而,如果請求中包含multipart,你的上下文中定義的MultipartResolver就會解析它。這樣,你請求中的multipart屬性就會象其它屬性一樣被處理。
12.8.2.?使用MultipartResolver
下面的例子說明了如何使用CommonsMultipartResolver:
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"><!-- one of the properties available; the maximum file size in bytes --><property name="maximumFileSize"><value>100000</value></property> </bean>這個例子使用CosMultipartResolver:
<bean id="multipartResolver" class="org.springframework.web.multipart.cos.CosMultipartResolver"><!-- one of the properties available; the maximum file size in bytes --><property name="maximumFileSize"><value>100000</value></property> </bean>當然你需要在你的classpath中為multipart解析器提供正確的jar文件。如果是CommonsMultipartResolver,你需要使用commons-fileupload.jar,如果是CosMultipartResolver,使用cos.jar。
你已經看到如何設置Spring處理multipart請求,接下來我們看看如何使用它。當Spring的DispatchServlet發現multipart請求時,它會激活定義在上下文中的解析器并處理請求。它通常做的就是將當前的HttpServletRequest封裝到支持multipart的MultipartHttpServletRequest。使用MultipartHttpServletRequest,你可以獲取請求所包含的multipart信息,在控制器中獲取具體的multipart內容。
12.8.3.?在一個表單中處理multipart
在MultipartResolver完成multipart解析后,multipart請求就會和其它請求一樣被處理。使用multipart,你需要創建一個帶文件上傳域的表單,讓Spring將文件綁定到你的表單上。就象其它不會自動轉換成String或基本類型的屬性一樣,為了將二進制數據放到你的bean中,你必須用ServletRequestDatabinder注冊一個自定義的編輯器。Spring有許多編輯器可以用來處理文件,以及在bean中設置結果。StringMultipartEditor能將文件轉換成String(使用用戶定義的字符集),ByteArrayMultipartEditor能將文件轉換成字節數組。它們就象CustomDateEditor一樣工作。
所以,為了在網站中使用表單上傳文件,需要聲明解析器,將URL映射到控制器,以及處理bean的控制器本身。
<beans>...<bean id="multipartResolver"class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/><bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"><property name="mappings"><props><prop key="/upload.form">fileUploadController</prop></props></property></bean><bean id="fileUploadController" class="examples.FileUploadController"><property name="commandClass"><value>examples.FileUploadBean</value></property><property name="formView"><value>fileuploadform</value></property><property name="successView"><value>confirmation</value></property></bean></beans>然后,創建控制器和含有文件屬性的bean
// snippet from FileUploadController public class FileUploadController extends SimpleFormController {protected ModelAndView onSubmit(HttpServletRequest request,HttpServletResponse response,Object command,BindException errors)throws ServletException, IOException {// cast the beanFileUploadBean bean = (FileUploadBean)command;// let's see if there's content therebyte[] file = bean.getFile();if (file == null) {// hmm, that's strange, the user did not upload anything}// well, let's do nothing with the bean for now and return: return super.onSubmit(request, response, command, errors);}protected void initBinder(HttpServletRequest request,ServletRequestDataBinder binder)throws ServletException {// to actually be able to convert Multipart instance to byte[]// we have to register a custom editor (in this case the// ByteArrayMultipartEditorbinder.registerCustomEditor(byte[].class, new ByteArrayMultipartFileEditor());// now Spring knows how to handle multipart object and convert them}}// snippet from FileUploadBean public class FileUploadBean {private byte[] file;public void setFile(byte[] file) {this.file = file;}public byte[] getFile() {return file;} }你會看到,FileUploadBean有一個byte[]類型的屬性來存放文件。控制器注冊一個自定義的編輯器以便讓Spring知道如何將解析器發現的multipart對象轉換成bean指定的屬性。在這些例子中,沒有對bean的byte[]類型的屬性做任何處理,但是在實際中可以做任何你想做的(將文件存儲在數據庫中,通過電子郵件發送給某人,等等)。
但是我們還沒有結束。為了讓用戶能真正上傳些東西,我們必須創建表單:
<html><head><title>Upload a file please</title></head><body><h1>Please upload a file</h1><form method="post" action="upload.form" enctype="multipart/form-data"><input type="file" name="file"/><input type="submit"/></form></body> </html>你可以看到,我們在bean的byte[]類型的屬性后面創建了一個域。我們還添加了編碼屬性以便讓瀏覽器知道如何編碼multipart的域(千萬不要忘記!)現在就可以工作了。
12.9.?處理異常
Spring提供了HandlerExceptionResolvers來幫助處理控制器處理你的請求時所發生的異常。HandlerExceptionResolvers在某種程度上和你在web應用的web.xml中定義的異常映射很相象。然而,它們提供了一種更靈活的處理異常的方式。首先,HandlerExceptionResolver通知你當異常拋出時如何處理。并且,這種可編程的異常處理方式使得在請求被傳遞到另一個URL前給了你更多的響應選擇。(這就和使用servlet特定異常映射的情況一樣)。
實現HandlerExceptionResolver需要實現resolveException(Exception, Handler)方法并返回ModelAndView,除了HandlerExceptionResolver,你還可以使用SimpleMappingExceptionResolver。這個解析器使你能夠獲取任何拋出的異常的類名,并將它映射到視圖名。這和servlet API的異常映射在功能上是等價的,但是它還為不同的處理器拋出的異常做更細粒度的映射提供可能。
12.10.?共同用到的工具
12.10.1.?關于pathmatcher的小故事
ToDo
from:?http://docs.huihoo.com/spring/zh-cn/mvc.html
總結
以上是生活随笔為你收集整理的Spring - Java/J2EE Application Framework 应用框架 第 12 章 Web框架的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring - Java/J2EE A
- 下一篇: Spring - Java/J2EE A