使用处理器映射,你可以将web请求映射到正确的处理器上。有很多处理器映射你可以使用,例如:SimpleUrlHandlerMapping或者BeanNameUrlHandlerMapping,但是先让我们看一下HandlerMapping的基本概念。
一个基本的HandlerMapping所提供的功能是将请求传递到HandlerExecutionChain上,首先HandlerExecutionChain包含一个符合输入请求的处理器。其次(但是可选的)是一个可以拦截请求的拦截器列表。当收到请求,DispatcherServlet将请求交给处理器映射,让它检查请求并获得一个正确的HandlerExecutionChain。然后,执行定义在执行链中的处理器和拦截器(如果有拦截器的话)
包含拦截器(处理器执行前,执行后,或者执行前后)的可配置的处理器映射功能非常强大。许多功能被放置在自定义的HandlerMappings中。一个自定义的处理器映射不仅根据请求的URL,而且还可以根据和请求相关的会话状态来选择处理器。
我们来看看Spring提供的处理器映射。
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!
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的处理器映射。
添加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。