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