?
This document uses PHP Chinese website manual Release
通過(guò)處理器映射,可以將web請(qǐng)求映射到正確的處理器(handler)上。
Spring內(nèi)置了很多處理器映射策略,例如:SimpleUrlHandlerMapping
或者BeanNameUrlHandlerMapping
。
現(xiàn)在我們先來(lái)看一下HandlerMapping
的基本概念。
HandlerMapping
的基本功能是將請(qǐng)求傳遞到HandlerExecutionChain
上。
首先,這個(gè)HandlerExecutionChain
必須包含一個(gè)能處理該請(qǐng)求的處理器。
其次,這個(gè)鏈也可以包含一系列可以攔截請(qǐng)求的攔截器。
當(dāng)收到請(qǐng)求時(shí),DispatcherServlet
將請(qǐng)求交給處理器映射,讓它檢查請(qǐng)求并找到一個(gè)適當(dāng)?shù)?code class="literal">HandlerExecutionChain。
然后,DispatcherServlet
執(zhí)行定義在鏈中的處理器和攔截器(interceptor)。
在處理器映射中通過(guò)配置攔截器(包括處理器執(zhí)行前、執(zhí)行后、或者執(zhí)行前后運(yùn)行攔截器)將使其功能更強(qiáng)大。
同時(shí)也可以通過(guò)自定義HandlerMapping
來(lái)支持更多的功能。
比如,一個(gè)自定義的處理器映射不僅可以根據(jù)請(qǐng)求的URL,而且還可以根據(jù)和請(qǐng)求相關(guān)的特定session狀態(tài)來(lái)選擇處理器。
下面我們將講述Spring中最常用的兩個(gè)處理器映射。
它們都是AbstractHandlerMapping
的子類(lèi),同時(shí)繼承了下面這些屬性:
interceptors
: 在映射中使用的攔截器列表。
HandlerInterceptor
將在第?13.4.3?節(jié) “攔截器(HandlerInterceptor
)”這一節(jié)講述。
defaultHandler
: 缺省的處理器。
當(dāng)沒(méi)有合適的處理器可以匹配請(qǐng)求時(shí),該處理器就會(huì)被使用。
order
: 根據(jù)每個(gè)映射的order屬性值 (由org.springframework.core.Ordered
接口定義),Spring 將上下文中可用的映射進(jìn)行排序,然后選用第一個(gè)和請(qǐng)求匹配的處理器。
alwaysUseFullPath
:如果這個(gè)屬性被設(shè)成true
,Spring 將會(huì)使用絕對(duì)路徑在當(dāng)前的servlet context中尋找合適的處理器。
這個(gè)屬性的默認(rèn)值是false
,在這種情況下,Spring會(huì)使用當(dāng)前servlet context中的相對(duì)路徑。
例如,如果一個(gè)servlet在servlet-mapping中用的值是/testing/*
,當(dāng)alwaysUseFullPath
設(shè)成true時(shí),
處理器映射中的URL格式應(yīng)該使用/testing/viewPage.html
,當(dāng)這個(gè)屬性設(shè)成false,同一個(gè)URL應(yīng)該寫(xiě)成
/viewPage.html
。
urlDecode
:這個(gè)屬性的默認(rèn)值是true
,和2.5版本一樣。
如果想比較編碼后的路徑,可以把這個(gè)屬性設(shè)為false
。
不過(guò),需要注意的是,HttpServletRequest
總是返回解碼后的servlet路徑,
與編碼后的格式進(jìn)行比較時(shí)可能不會(huì)匹配。
lazyInitHandlers
:這個(gè)屬性允許設(shè)置是否延遲singleton處理器的初始化工作(prototype處理器的初始化都是延遲的)。
這個(gè)屬性的默認(rèn)值是false
。
(注意:最后三個(gè)屬性只有org.springframework.web.servlet.handler.AbstractUrlHandlerMapping
的子類(lèi)才有。)
BeanNameUrlHandlerMapping
是一個(gè)簡(jiǎn)單但很強(qiáng)大的處理器映射,它將收到的HTTP請(qǐng)求映射到bean的名稱(chēng)(這些bean需要在web應(yīng)用上下文中定義)。
例如,為了實(shí)現(xiàn)一個(gè)用戶新建賬號(hào)的功能,我們提供了FormController
(關(guān)于CommandController和FormController請(qǐng)參考第?13.3.4?節(jié) “命令控制器”)和顯示表單的JSP視圖(或Velocity模版)。
當(dāng)使用BeanNameUrlHandlerMapping
時(shí),我們用如下方式將包含http://samples.com/editaccount.form
的訪問(wèn)請(qǐng)求映射到指定的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"/> <property name="successView" value="account-created"/> <property name="commandName" value="account"/> <property name="commandClass" value="samples.Account"/> </bean> <beans>
所有對(duì)/editaccount.form
的請(qǐng)求就會(huì)由上面的FormController處理。
當(dāng)然我們得在web.xml
中定義servlet-mapping,接受所有以.form
結(jié)尾的請(qǐng)求。
<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
,無(wú)須(如上所示)在web應(yīng)用上下文中定義它。
缺省情況下,如果在上下文中沒(méi)有找到處理器映射,DispatcherServlet
會(huì)為你創(chuàng)建一個(gè)BeanNameUrlHandlerMapping
!
另一個(gè)更加強(qiáng)大的處理器映射是SimpleUrlHandlerMapping
。
它在應(yīng)用上下文中可以進(jìn)行配置,并且有Ant風(fēng)格的路徑匹配功能。
(請(qǐng)參考org.springframework.util.PathMatcher
的JavaDoc)。下面幾個(gè)例子可以幫助理解:
<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> <!-- maps the sample dispatcher to *.html --> <servlet-mapping> <servlet-name>sample</servlet-name> <url-pattern>*.html</url-pattern> </servlet-mapping> ... </web-app>
上面的web.xml設(shè)置允許所有以.html
和.form
結(jié)尾的請(qǐng)求都由這個(gè)sample DispatcherServlet
處理。
<beans>
<!-- no 'id'
required, HandlerMapping
beans are automatically detected by the DispatcherServlet
-->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<value>
/*/account.form=editAccountFormController
/*/editaccount.form=editAccountFormController
/ex/view*.html=helpController
/**/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>
這個(gè)處理器映射首先將對(duì)所有目錄中文件名為help.html
的請(qǐng)求傳遞給helpController
。
helpController
是一個(gè)UrlFilenameViewController
(要了解更多關(guān)于控制器的信息,請(qǐng)參閱第?13.3?節(jié) “控制器”)。
對(duì)ex
目錄中所有以view
開(kāi)始,以.html
結(jié)尾的請(qǐng)求都會(huì)被傳遞給helpController
。
同樣的,我們也為editAccountFormController
定義了兩個(gè)映射。
Spring的處理器映射支持?jǐn)r截器。當(dāng)你想要為某些請(qǐng)求提供特殊功能時(shí),例如對(duì)用戶進(jìn)行身份認(rèn)證,這就非常有用。
處理器映射中的攔截器必須實(shí)現(xiàn)org.springframework.web.servlet
包中的HandlerInterceptor
接口。
這個(gè)接口定義了三個(gè)方法,一個(gè)在處理器執(zhí)行前被調(diào)用,一個(gè)在處理器執(zhí)行后被調(diào)用,另一個(gè)在整個(gè)請(qǐng)求處理完后調(diào)用。
這三個(gè)方法提供你足夠的靈活度做任何處理前后的操作。
preHandle(..)
方法有一個(gè)boolean返回值。
使用這個(gè)值,可以調(diào)整執(zhí)行鏈的行為。
當(dāng)返回true
時(shí),處理器執(zhí)行鏈將繼續(xù)執(zhí)行,當(dāng)返回false
時(shí),DispatcherServlet
認(rèn)為該攔截器已經(jīng)處理完了請(qǐng)求(比如顯示正確的視圖),而不繼續(xù)執(zhí)行執(zhí)行鏈中的其它攔截器和處理器。
下面的例子提供了一個(gè)攔截器,它攔截所有請(qǐng)求,如果當(dāng)前時(shí)間不是在上午9點(diǎn)到下午6點(diǎn),它將用戶重定向到某個(gè)頁(yè)面。
<beans> <bean id="handlerMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="interceptors"> <list> <ref bean="officeHoursInterceptor"/> </list> </property> <property name="mappings"> <value> /*.form=editAccountFormController /*.view=editAccountFormController </value> </property> </bean> <bean id="officeHoursInterceptor" class="samples.TimeBasedAccessInterceptor"> <property name="openingTime" value="9"/> <property name="closingTime" value="18"/> </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; } } }
所有的請(qǐng)求都將被TimeBasedAccessInterceptor
截獲,
如果當(dāng)前時(shí)間不在上班時(shí)間,用戶會(huì)被重定向到一個(gè)靜態(tài)html頁(yè)面,提供諸如只有上班時(shí)間才能訪問(wèn)網(wǎng)站之類(lèi)的告示。
Spring還提供了一個(gè)adapter類(lèi)HandlerInterceptorAdapter
讓用戶更方便的擴(kuò)展HandlerInterceptor
接口。