?
Dokumen ini menggunakan Manual laman web PHP Cina Lepaskan
控制器的概念是MVC設(shè)計模式的一部分(確切地說,是MVC中的C)。應(yīng)用程序的行為通常被定義為服務(wù)接口, 而控制器使得用戶可以訪問應(yīng)用所提供的服務(wù)。控制器解析用戶輸入,并將其轉(zhuǎn)換成合理的模型數(shù)據(jù),從而可以進(jìn)一步由視圖展示給用戶。 Spring以一種抽象的方式實現(xiàn)了控制器概念,這樣可以支持不同類型的控制器。Spring本身包含表單控制器、命令控制器、向?qū)涂刂破鞯榷喾N多樣的控制器。
Spring控制器架構(gòu)的基礎(chǔ)是org.springframework.mvc.Controller
接口,其代碼如下:
public interface Controller { ModelAndView handleRequest( HttpServletRequest request, HttpServletResponse response) throws Exception; }
可以發(fā)現(xiàn)Controller
接口僅僅聲明了一個方法,它負(fù)責(zé)處理請求并返回合適的模型和視圖。Spring MVC實現(xiàn)的基礎(chǔ)
就是這三個概念:Mdel、View(ModelAndView
)以及 Controller
。雖然
Controller
接口是完全抽象的,但Spring也提供了許多你可能會用到的控制器。Controller接口僅僅定義了每個控制器都必須提供的基本功能:
處理請求并返回一個模型和一個視圖。
為提供一套基礎(chǔ)設(shè)施,所有的Spring控制器都繼承了 AbstractController
,AbstractController
提供了諸如緩存支持和mimetype設(shè)置這樣的功能。
表?13.3.?AbstractController
提供的功能
功能 | 描述 |
---|---|
supportedMethods |
指定這個控制器應(yīng)該接受什么樣的請求方法。通常它被設(shè)置成同時支持GET和POST,但是可以選擇你想支持的方法。如果控制器不支持請求發(fā)送的方法,
客戶端會得到通知(通常是拋出一個ServletException )。
|
requiresSession |
表明這個控制器是否需要HTTP session才能正常工作。如果控制器在沒有session的情況下接收到請求,客戶端會因為拋出ServletException
而得到通知。
|
synchronizeOnSession |
指定controller是否同步用戶的HTTP session。 |
cacheSeconds |
指定controller通知客戶端對數(shù)據(jù)內(nèi)容緩存的秒數(shù),一般為大于零的整數(shù)。默認(rèn)值為-1,即不緩存。 |
useExpiresHeader |
指定Controller在響應(yīng)請求時是否兼容HTTP 1.0 Expires header。缺省值為true 。 |
useCacheHeader |
指定Controller在相應(yīng)請求時是否兼容HTTP 1.1 Cache-Control header。默認(rèn)值為true 。 |
當(dāng)從AbstractController
繼承時,只需要實現(xiàn)handleRequestInternal(HttpServletRequest,
HttpServletResponse)
抽象方法,該方法將用來實現(xiàn)自定義的邏輯,并返回一個ModelAndView
對象。下面這個簡單的例子演示
了如何從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>
要讓該簡單控制器工作, 除創(chuàng)建一個handler mapping(請參考第?13.4?節(jié) “處理器映射(handler mapping)”一節(jié))外, 需要的全部就是上面的類和在web application context中的聲明。 該controller在再次檢查前,通知客戶端將響應(yīng)數(shù)據(jù)緩存2分鐘,并返回使用硬編碼的視圖名(盡管這樣做不好)。
盡管可以繼承AbstractController
來實現(xiàn)自己的控制器,不過Spring提供的眾多控制器減輕了我們開發(fā)簡單MVC應(yīng)用時的負(fù)擔(dān)。
ParameterizableViewController
基本上和上面例子中的一樣,不同的是,可以在application context中指定返回的視圖名稱(從而
避免了在Java代碼中的硬編碼)。
UrlFilenameViewController
會檢查URL,獲取文件請求的文件名,并把它作為視圖名加以使用。。例如,
http://www.springframework.org/index.html
對應(yīng)的視圖文件名是index
。
Spring提供了MultiActionController
來將多個請求處理方法合并在一個控制器里,這樣可以把相關(guān)功能組合在一起。
(如果你很熟悉Struts,會發(fā)現(xiàn)這與Struts的
位于DispatchAction
很像)
MultiActionControllerorg.springframework.web.mvc.multiaction
包中,它可以定義頁面請求到控制器方法名的映射,
然后在處理相應(yīng)請求時調(diào)用該方法。當(dāng)你有很多比較小的且相關(guān)的功能時使用MultiActionController
很方便,這樣就不必為每個小功能創(chuàng)建
一個單獨的Controller
了。但是一般來說MultiActionController
不適合處理復(fù)雜邏輯,或者完全不相關(guān)
的功能,這時應(yīng)該堅持使用標(biāo)準(zhǔn)方法,當(dāng)在一個控制器存在大量公共的行為,但是有多個調(diào)用入口時,使用MultiActionController
就特別方便。
MultiActionController
有兩種使用方式:一是創(chuàng)建MultiActionController
的子類,并指定將被
MethodNameResolver
解析的方法(這種情況下不需要這個delegate參數(shù));二是定義一個委托對象,
MethodNameResolver
解析出目標(biāo)方法后將調(diào)用該對象的相應(yīng)方法。這種情況下需要定義MultiActionController
的實例并將委托對象作為協(xié)作者注入(可通過構(gòu)造參數(shù)或者setDelegate
方法)。
MultiActionController
需要一種策略,使其可以通過解析請求信息來獲得要調(diào)用的方法。這個解析策略由
MethodNameResolver
接口定義。MultiActionController
提供了'methodNameResolver
'
屬性使得你可以注入需要的MethodNameResolver
。在自己的MultiActionController
(或者前面說的委托對象)
上定義的請求處理方法必須符合如下簽名:
// 'anyMeaningfulName
'指任意方法名
public [ModelAndView | Map | void] anyMeaningfulName(HttpServletRequest, HttpServletResponse [,HttpSession] [,AnyObject])
上述方法的詳細(xì)信息可參考
MultiActionController類 Javadoc。如果打算使用MultiActionController
,那最好看看它的Javadoc。不過,下面提供了
一些關(guān)于合法的請求處理方法的基本例子。
標(biāo)準(zhǔn)格式(跟Controller
接口定義的一樣)。
public ModelAndView displayCatalog(HttpServletRequest, HttpServletResponse)
下面這個方法接收Login
參數(shù),該參數(shù)中包含從請求中抽取出來的信息。
public ModelAndView login(HttpServletRequest, HttpServletResponse, Login)
下面這個方法要求請求中已經(jīng)存在合法的session對象。
public ModelAndView viewCart(HttpServletRequest, HttpServletResponse, HttpSession)
下面這個方法接受一個Product
參數(shù),這個參數(shù)包含從請求中抽取出來的信息,并且要求請求中已經(jīng)存在一個
合法的session對象。注意參數(shù)的順序很重要:session必須是第三個參數(shù),而綁定參數(shù)必須是final的,并位于session之后。
public ModelAndView updateCart(HttpServletRequest, HttpServletResponse, HttpSession, Product)
下面這個方法聲明返回void
類型,這說明它會直接寫response。
public void home(HttpServletRequest, HttpServletResponse)
下面這個方法返回Map
,表明視圖解析器應(yīng)該從請求中抽取視圖名,而返回數(shù)據(jù)將被放入model
(參考第?13.11?節(jié) “慣例優(yōu)先原則(convention over configuration)”)。
public Map list(HttpServletRequest, HttpServletResponse)
MethodNameResolver
負(fù)責(zé)從請求中解析出需要調(diào)用的方法名稱。Spring本身已經(jīng)提供了一系列
MethodNameResolver
的實現(xiàn),當(dāng)然也可以編寫自己的實現(xiàn)。注意,如果沒有明確注入自己的實現(xiàn),Spring默認(rèn)使用
InternalPathMethodNameResolver
。
InternalPathMethodNameResolver
-從請求路徑中獲取文件名作為方法名
比如,http://www.sf.net/testing.view
的請求會調(diào)用testing(HttpServletRequest,HttpServletResponse)
方法。
ParameterMethodNameResolver
- 解析請求參數(shù),并將它作為方法名。
比如,對應(yīng)http://www.sf.net/index.view?method=testIt
的請求,會調(diào)用
testIt(HttpServletRequest, HttpServletResponse)
方法)。使用paramName
屬性定義要使用的請求參數(shù)名稱。
PropertiesMethodNameResolver
- 使用用戶自定義的屬性(Properties)對象,將請求的URL映射到方法名。比如,當(dāng)屬性中包含
/index/welcome.html=doIt
時,對/index/welcome.html
的請求會調(diào)用
doIt(HttpServletRequest, HttpServletResponse)
方法。 PropertiesMethodNameResolver
內(nèi)部使用了
Spring的PathMatcher
,所以支持路徑通配符,比如上邊那個URL寫成welcom?.html
也是可以的。
可以聲明自己的方法來處理請求處理過程中產(chǎn)生的Exceptions
。該方法的簽名與請求處理方法的簽名類似:第一個參數(shù)必須是
HttpServletRequest
,第二個參數(shù)必須是HttpServletResponse
。不過與請求處理
方法不同的是,該方法的名字可以任意,具體匹配策略由該方法的第三個參數(shù)(參數(shù)類型必須是一種Exception
)決定。Spring根據(jù)最接近的
異常類型進(jìn)行匹配。下面是一個這種異常處理方法簽名的例子:
public ModelAndView processException(HttpServletRequest, HttpServletResponse, IllegalArgumentException)
我們來看一個例子,其中展示了MultiActionController
與ParameterMethodNameResolver
一同使用的委托方式。
<bean id="paramMultiController" class="org.springframework.web.servlet.mvc.multiaction.MultiActionController"> <property name="methodNameResolver"> <bean class="org.springframework.web.servlet.mvc.multiaction.ParameterMethodNameResolver"> <property name="paramName" value="method"/> </bean> </property> <property name="delegate"> <bean class="samples.SampleDelegate"/> </property> </bean> }
public class SampleDelegate { public ModelAndView retrieveIndex(HttpServletRequest req, HttpServletResponse resp) { return new ModelAndView("index", "date", new Long(System.currentTimeMillis())); } }
當(dāng)使用上述的委托方式時,我們需要配置PropertiesMethodNameResolver
,來完成與我們定義的方法的任意數(shù)量的URL的匹配。
<bean id="propsResolver" class="org....mvc.multiaction.PropertiesMethodNameResolver"> <property name="mappings"> <value> /index/welcome.html=retrieveIndex notwelcome.html=retrieveIndex account.form=editAccountFormController help.html=helpController </value> </property> </bean> <bean id="helpController" class="org.springframework.web.servlet.mvc.UrlFilenameViewController"/> <bean id="editAccountFormController" class="org.springframework.web.servlet.mvc.SimpleFormController"> <property name="formView" value="account"/> <property name="successView" value="account-created"/> <property name="commandName" value="Account"/> <property name="commandClass" value="samples.Account"/> </bean> <beans>
這個處理器映射首先將對所有目錄中文件名為help.html
的請求傳遞給helpController
。
helpController
是一個UrlFilenameViewController
(要了解更多關(guān)于控制器的信息,請參閱第?13.3?節(jié) “控制器”)。
對ex
目錄中所有以view
開始,以.html
結(jié)尾的請求都會被傳遞給helpController
。
同樣的,我們也為editAccountFormController
定義了兩個映射。
Spring的處理器映射支持?jǐn)r截器。當(dāng)你想要為某些請求提供特殊功能時,例如對用戶進(jìn)行身份認(rèn)證,這就非常有用。
處理器映射中的攔截器必須實現(xiàn)org.springframework.web.servlet
包中的HandlerInterceptor
接口。
這個接口定義了三個方法,一個在處理器執(zhí)行前被調(diào)用,一個在處理器執(zhí)行后被調(diào)用,另一個在整個請求處理完后調(diào)用。
這三個方法提供你足夠的靈活度做任何處理前后的操作。
preHandle(..)
方法有一個boolean返回值。
使用這個值,可以調(diào)整執(zhí)行鏈的行為。
當(dāng)返回true
時,處理器執(zhí)行鏈將繼續(xù)執(zhí)行,當(dāng)返回false
時,DispatcherServlet
認(rèn)為該攔截器已經(jīng)處理完了請求(比如顯示正確的視圖),而不繼續(xù)執(zhí)行執(zhí)行鏈中的其它攔截器和處理器。
下面的例子提供了一個攔截器,它攔截所有請求,如果當(dāng)前時間不是在上午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"> <value> *.view=someController</value> </property> </bean>
在上面這個例子中,所有對*.view
資源的請求,只要包含參數(shù)siteLanguage
,
都會改變本地化信息。比如下面這個請求http://www.sf.net/home.view?siteLanguage=nl
會將網(wǎng)站語言修改為荷蘭語。