控制器的概念是MVC設(shè)計(jì)模式的一部分(確切地說(shuō),是MVC中的C)。應(yīng)用程序的行為通常被定義為服務(wù)接口,而控制器使得用戶(hù)可以訪(fǎng)問(wèn)應(yīng)用所提供的服務(wù)。控制器解析用戶(hù)輸入,并將其轉(zhuǎn)換成合理的模型數(shù)據(jù),從而可以進(jìn)一步由視圖展示給用戶(hù)。Spring以一種抽象的方式實(shí)現(xiàn)了控制器概念,這樣使得不同類(lèi)型的控制器可以被創(chuàng)建。Spring本身包含表單控制器、命令控制器、向?qū)涂刂破鞯榷喾N多樣的控制器。
Spring控制器架構(gòu)的基礎(chǔ)是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; }
你可以發(fā)現(xiàn)Controller
接口僅僅聲明了一個(gè)方法,它負(fù)責(zé)處理請(qǐng)求并返回合適的模型和視圖。Spring MVC實(shí)現(xiàn)的基礎(chǔ)就是這三個(gè)概念:Mdel、View(ModelAndView
)以及 Controller
。雖然Controller
接口是完全抽象的,但Spring也提供了許多你可能會(huì)用到的控制器。Controller接口僅僅定義了每個(gè)控制器都必須提供的基本功能:處理請(qǐng)求并返回一個(gè)模型和一個(gè)視圖。
為了提供一套基礎(chǔ)設(shè)施,所有的Spring控制器都繼承了 AbstractController
,AbstractController
提供了諸如緩存支持和mimetype設(shè)置這樣的功能。
表?13.3.?AbstractController
提供的功能
功能 | 描述 |
---|---|
supportedMethods
|
指定這個(gè)控制器應(yīng)該接受什么樣的請(qǐng)求方法。通常它被設(shè)置成同時(shí)支持GET和POST,但是你可以選擇你想支持的方法。如果控制器不支持請(qǐng)求發(fā)送的方法,客戶(hù)端會(huì)得到通知(通常是拋出一個(gè)ServletException )。 |
requiresSession
|
指定這個(gè)控制器是否需要HTTP session才能正常工作。如果控制器在沒(méi)有session的情況下接收到請(qǐng)求,客戶(hù)端會(huì)因?yàn)閽伋?code class="classname">ServletException而得到通知。 |
synchronizeSession
|
指定controller是否同步用戶(hù)的HTTP session。 |
cacheSeconds
|
指定controller通知客戶(hù)端對(duì)數(shù)據(jù)內(nèi)容緩存的秒數(shù),一般為大于零的整數(shù)。默認(rèn)值為-1,即不緩存。 |
useExpiresHeader
|
指定Controller在響應(yīng)請(qǐng)求時(shí)是否兼容HTTP 1.0 Expires header。缺省值為true 。 |
useCacheHeader
|
指定Controller在相應(yīng)請(qǐng)求時(shí)是否兼容HTTP 1.1 Cache-Control header。默認(rèn)值為true 。 |
當(dāng)從AbstractController
繼承時(shí),需要實(shí)現(xiàn)handleRequestInternal(HttpServletRequest, HttpServletResponse)
抽象方法,該方法將用來(lái)實(shí)現(xiàn)自己的邏輯,并返回一個(gè)ModelAndView
對(duì)象。下面這個(gè)簡(jiǎn)單的例子演示了如何從AbstractController
繼承以及如何在applicationContext.xml中進(jìn)行配置
package samples; public class SampleController extends AbstractController { public ModelAndView handleRequestInternal( HttpServletRequest request, HttpServletResponse response) throws Exception { ModelAndView mav = new ModelAndView("hello"); mav.addObject("message", "Hello World!"); return mav; } }
<bean id="sampleController" class="samples.SampleController"> <property name="cacheSeconds" value="120"/> </bean>
該controller返回的ModelAndView使用了硬編碼的視圖名(盡管這樣做不好),并通知客戶(hù)端將響應(yīng)數(shù)據(jù)緩存2分鐘。除了通過(guò)以上方式創(chuàng)建和配置controller之外,還需要配置handler mapping(請(qǐng)參考第?13.4?節(jié) “處理器映射(handler mapping)”),這樣該controller就可以工作了。
盡管可以繼承AbstractController
來(lái)實(shí)現(xiàn)自己的控制器,不過(guò)Spring提供的眾多控制器減輕了我們開(kāi)發(fā)簡(jiǎn)單MVC應(yīng)用時(shí)的負(fù)擔(dān)。ParameterizableViewController
基本上和上面例子中的一樣,不同的是,你可以在applicationContext.xml配置中指定返回視圖名從而避免了在Java代碼中的硬編碼。
UrlFilenameViewController
會(huì)檢查URL,獲取文件請(qǐng)求的文件名,并把它作為視圖名加以使用。。例如,http://www.springframework.org/index.html
對(duì)應(yīng)的視圖文件名是index
。
MultiActionController
將多個(gè)行為(action)合并在一個(gè)控制器里,這樣可以把相關(guān)功能組合在一起。MultiActionController
位于org.springframework.web.mvc.multiaction
包中,它通過(guò)將請(qǐng)求映射到正確的方法名來(lái)調(diào)用方法。當(dāng)在一個(gè)控制器存在大量公共的行為,但是有多個(gè)調(diào)用入口時(shí),使用MultiActionController
就特別方便。
表?13.4.?MultiActionController
提供的功能
功能 | 描述 |
---|---|
delegate
|
MultiActionController 有兩種使用方式。第一種是你繼承MultiActionController ,并在子類(lèi)中指定由MethodNameResolver 解析的方法(這種情況下不需要這個(gè)delegate參數(shù))。第二種是你定義一個(gè)代理對(duì)象,由它提供MethodNameResolver 解析出來(lái)的方法(這種情況下,你必須使用這個(gè)配置參數(shù)定義代理對(duì)象)。 |
methodNameResolver
|
MultiActionController 需要一種策略,使其可以通過(guò)解析請(qǐng)求信息來(lái)獲得要調(diào)用的方法。這個(gè)解析策略由MethodNameResolver 這個(gè)接口定義的。這個(gè)參數(shù)允許你實(shí)現(xiàn)MethodNameResolver 接口,然后在控制器中使用你的策略。 |
MultiActionController
所支持的方法需要符合下列格式:
// anyMeaningfulName can be replaced by any methodname
public [ModelAndView | Map | void] anyMeaningfulName(HttpServletRequest, HttpServletResponse [, Exception | AnyObject]);
注意:在此不允許方法重載,因?yàn)?code class="classname">MultiActionController無(wú)法分辨出重載(overloading)了的方法。此外,你可以定義exception handler來(lái)處理方法中拋出的異常。
Exception
參數(shù)是可選的,它可以是任何異常,只要它是java.lang.Exception
或java.lang.RuntimeException
的子類(lèi)。AnyObject
參數(shù)也是可選的,它可以是任何對(duì)象。HTTP Request中的參數(shù)會(huì)存在這個(gè)對(duì)象中,以便使用。
下面幾個(gè)例子示范了MultiActionController
正確的方法定義。
標(biāo)準(zhǔn)格式(跟Controller
接口定義的一樣)。
public ModelAndView doRequest(HttpServletRequest, HttpServletResponse)
下面這個(gè)方法支持Login
參數(shù), 這個(gè)參數(shù)中包含從請(qǐng)求中抽取出來(lái)的信息。
public ModelAndView doLogin(HttpServletRequest, HttpServletResponse, Login)
下面這個(gè)方法可以處理Exception
。
public ModelAndView processException(HttpServletRequest, HttpServletResponse, IllegalArgumentException)
下面這個(gè)方法不返回任何數(shù)值。 (請(qǐng)參考后面的章節(jié) 第?13.11?節(jié) “慣例優(yōu)先原則(convention over configuration)”)
public void goHome(HttpServletRequest, HttpServletResponse)
This signature has a Map
return type (see the section entitled 第?13.11?節(jié) “慣例優(yōu)先原則(convention over configuration)” below).
下面這個(gè)方法返回一個(gè)Map
。 (請(qǐng)參考后面的章節(jié)第?13.11?節(jié) “慣例優(yōu)先原則(convention over configuration)”)
public Map doRequest(HttpServletRequest, HttpServletResponse)
MethodNameResolver
負(fù)責(zé)從請(qǐng)求中解析出需要調(diào)用的方法名稱(chēng)。下面是Spring中內(nèi)置的三個(gè)MethodNameResolver
實(shí)現(xiàn)。
-
ParameterMethodNameResolver
- 解析請(qǐng)求參數(shù),并將它作為方法名。(對(duì)應(yīng)http://www.sf.net/index.view?testParam=testIt
的請(qǐng)求,會(huì)調(diào)用testIt(HttpServletRequest,HttpServletResponse)
方法)。使用paramName
配置參數(shù),可以設(shè)定要檢查的參數(shù)。 -
InternalPathMethodNameResolver
-從路徑中獲取文件名作為方法名 (http://www.sf.net/testing.view
的請(qǐng)求會(huì)調(diào)用testing(HttpServletRequest,HttpServletResponse)
方法。 -
PropertiesMethodNameResolver
- 使用用戶(hù)自定義的屬性對(duì)象,將請(qǐng)求的URL映射到方法名。當(dāng)屬性中包含/index/welcome.html=doIt
時(shí),發(fā)到/index/welcome.html
的請(qǐng)求會(huì)調(diào)用doIt(HttpServletRequest, HttpServletResponse)
這個(gè)方法。 這個(gè)方法名解析器可以和PathMatcher
一起工作,比如上邊那個(gè)URL寫(xiě)成/**/welcom?.html
也是可以的。
我們來(lái)看一組例子。首先是一個(gè)使用ParameterMethodNameResolver
和代理(delegate)屬性的例子,它接受包含參數(shù)名"method"的請(qǐng)求,調(diào)用方法retrieveIndex
:
<bean id="paramResolver" class="org....mvc.multiaction.ParameterMethodNameResolver">
<property name="paramName" value="method"/>
</bean>
<bean id="paramMultiController" class="org....mvc.multiaction.MultiActionController">
<property name="methodNameResolver" ref="paramResolver"/>
<property name="delegate" ref="sampleDelegate"/>
</bean>
<bean id="sampleDelegate" class="samples.SampleDelegate"/>
## together with
public class SampleDelegate {
public ModelAndView retrieveIndex(HttpServletRequest req, HttpServletResponse resp) {
return new ModelAndView("index", "date", new Long(System.currentTimeMillis()));
}
}
當(dāng)使用上面的代理對(duì)象時(shí),我們也可以使用PropertiesMethodNameRseolver
來(lái)匹配一組URL,將它們映射到我們定義的方法上:
<bean id="propsResolver" class="org....mvc.multiaction.PropertiesMethodNameResolver"> <property name="mappings"> <value> /index/welcome.html=retrieveIndex /**/notwelcome.html=retrieveIndex /*/user?.html=retrieveIndex </value> </property> </bean> <bean id="paramMultiController" class="org....mvc.multiaction.MultiActionController"> <property name="methodNameResolver" ref="propsResolver"/> <property name="delegate" ref="sampleDelegate"/> </bean>
Spring的CommandController是Spring MVC的重要部分。命令控制器提供了一種和數(shù)據(jù)對(duì)象交互的方式,并動(dòng)態(tài)地將來(lái)自HttpServletRequest
的參數(shù)綁定到你指定的數(shù)據(jù)對(duì)象上。它的功能和Struts中的ActionForm
有點(diǎn)像,不過(guò)在Spring中,你不需要實(shí)現(xiàn)任何接口來(lái)實(shí)現(xiàn)數(shù)據(jù)綁定。首先,讓我們看一下有哪些可以使用的命令控制器:
-
AbstractCommandController
--你可以使用該抽象命令控制器來(lái)創(chuàng)建自己的命令控制器,它能夠?qū)⒄?qǐng)求參數(shù)綁定到你指定的命令對(duì)象。這個(gè)類(lèi)并不提供任何表單功能,但是它提供驗(yàn)證功能,并且讓你在子類(lèi)中去實(shí)現(xiàn)如何處理由請(qǐng)求參數(shù)產(chǎn)生的命令對(duì)象。 -
AbstractFormController
--一個(gè)支持表單提交的抽象控制器類(lèi)。使用這個(gè)控制器,你可以定義表單,并使用從控制器獲取的數(shù)據(jù)對(duì)象構(gòu)建表單。當(dāng)用戶(hù)輸入表單內(nèi)容,AbstractFormController
將用戶(hù)輸入的內(nèi)容綁定到命令對(duì)象,驗(yàn)證表單內(nèi)容,并將該對(duì)象交給控制器,完成相應(yīng)的操作。它支持的功能有防止重復(fù)提交、表單驗(yàn)證以及一般的表單處理流程。子類(lèi)需要實(shí)現(xiàn)自己的方法來(lái)指定采用哪個(gè)視圖來(lái)顯示輸入表單,哪個(gè)視圖顯示表單正確提交后的結(jié)果。如果你需要表單,但不想在應(yīng)用上下文中指定顯示給用戶(hù)的視圖,就使用這個(gè)控制器。 -
SimpleFormController
--這是一個(gè)form cotnroller,當(dāng)需要根據(jù)命令對(duì)象來(lái)創(chuàng)建相應(yīng)的form的時(shí)候,該類(lèi)可以提供更多的支持。你可以為其指定一個(gè)命令對(duì)象,顯示表單的視圖名,當(dāng)表單提交成功后顯示給用戶(hù)的視圖名等等。 -
AbstractWizardFormController
--這是一個(gè)抽象類(lèi),繼承這個(gè)類(lèi)需要實(shí)現(xiàn)validatePage()
、processFinish()
和processCancel()
方法。你有可能也需要寫(xiě)一個(gè)構(gòu)造器,它至少需要調(diào)用
setPages()
和setCommandName()
方法。setPages()的參數(shù)是一個(gè)String數(shù)組,這個(gè)數(shù)組包含了組成向?qū)У囊晥D名。setCommandName()的參數(shù)是一個(gè)String,該參數(shù)將用來(lái)在視圖中調(diào)用你的命令對(duì)象。和
AbstractFormController
的實(shí)現(xiàn)一樣, 你需要使用命令對(duì)象(其實(shí)就是一個(gè)JavaBean, 這個(gè)bean中包含了表單的信息)。你有兩個(gè)選擇:在構(gòu)造函數(shù)中調(diào)用setCommandClass()
方法(參數(shù)是命令對(duì)象的類(lèi)名),或者實(shí)現(xiàn)formBackingObject()
方法。AbstractWizardFormController
有幾個(gè)你可以復(fù)寫(xiě)(override)的方法。最有用的一個(gè)是referenceData(..)
。這個(gè)方法允許你把模型數(shù)據(jù)以Map
的格式傳遞給視圖;getTargetPage()
允許你動(dòng)態(tài)地更改向?qū)У捻?yè)面順序,或者直接跳過(guò)某些頁(yè)面;onBindAndValidate()
允許你復(fù)寫(xiě)內(nèi)置的綁定和驗(yàn)證流程。最后,我們有必要提一下
setAllowDirtyBack()
和setAllowDirtyForward()
兩個(gè)方法。 你可以在getTargetPage()
中調(diào)用這兩個(gè)方法,這兩個(gè)方法將決定在當(dāng)前頁(yè)面驗(yàn)證失敗時(shí),是否允許向?qū)耙苹蚝笸恕?/p>AbstractWizardFormController
的更詳細(xì)內(nèi)容請(qǐng)參考JavaDoc。在Spring附帶的例子jPetStore中,有一個(gè)關(guān)于向?qū)?shí)現(xiàn)的例子:org.springframework.samples.jpetstore.web.spring.OrderFormController
。
大盤(pán)預(yù)測(cè) 國(guó)富論