©
本文档使用
php中文网手册 发布
控制器的概念是MVC设计模式的一部分(确切地说,是MVC中的C)。应用程序的行为通常被定义为服务接口, 而控制器使得用户可以访问应用所提供的服务。控制器解析用户输入,并将其转换成合理的模型数据,从而可以进一步由视图展示给用户。 Spring以一种抽象的方式实现了控制器概念,这样可以支持不同类型的控制器。Spring本身包含表单控制器、命令控制器、向导型控制器等多种多样的控制器。
Spring控制器架构的基础是org.springframework.mvc.Controller接口,其代码如下:
public interface Controller {
ModelAndView handleRequest(
HttpServletRequest request,
HttpServletResponse response) throws Exception;
}
可以发现Controller接口仅仅声明了一个方法,它负责处理请求并返回合适的模型和视图。Spring MVC实现的基础
就是这三个概念:Mdel、View(ModelAndView)以及 Controller。虽然
Controller接口是完全抽象的,但Spring也提供了许多你可能会用到的控制器。Controller接口仅仅定义了每个控制器都必须提供的基本功能:
处理请求并返回一个模型和一个视图。
为提供一套基础设施,所有的Spring控制器都继承了 AbstractController ,AbstractController
提供了诸如缓存支持和mimetype设置这样的功能。
表 13.3. AbstractController提供的功能
| 功能 | 描述 |
|---|---|
supportedMethods |
指定这个控制器应该接受什么样的请求方法。通常它被设置成同时支持GET和POST,但是可以选择你想支持的方法。如果控制器不支持请求发送的方法,
客户端会得到通知(通常是抛出一个ServletException)。
|
requiresSession |
表明这个控制器是否需要HTTP session才能正常工作。如果控制器在没有session的情况下接收到请求,客户端会因为抛出ServletException
而得到通知。
|
synchronizeOnSession |
指定controller是否同步用户的HTTP session。 |
cacheSeconds |
指定controller通知客户端对数据内容缓存的秒数,一般为大于零的整数。默认值为-1,即不缓存。 |
useExpiresHeader |
指定Controller在响应请求时是否兼容HTTP 1.0 Expires header。缺省值为true。 |
useCacheHeader |
指定Controller在相应请求时是否兼容HTTP 1.1 Cache-Control header。默认值为true。 |
当从AbstractController继承时,只需要实现handleRequestInternal(HttpServletRequest,
HttpServletResponse)抽象方法,该方法将用来实现自定义的逻辑,并返回一个ModelAndView对象。下面这个简单的例子演示
了如何从AbstractController继承以及如何在applicationContext.xml中进行配置。
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>
要让该简单控制器工作, 除创建一个handler mapping(请参考第 13.4 节 “处理器映射(handler mapping)”一节)外, 需要的全部就是上面的类和在web application context中的声明。 该controller在再次检查前,通知客户端将响应数据缓存2分钟,并返回使用硬编码的视图名(尽管这样做不好)。
尽管可以继承AbstractController来实现自己的控制器,不过Spring提供的众多控制器减轻了我们开发简单MVC应用时的负担。
ParameterizableViewController基本上和上面例子中的一样,不同的是,可以在application context中指定返回的视图名称(从而
避免了在Java代码中的硬编码)。
UrlFilenameViewController会检查URL,获取文件请求的文件名,并把它作为视图名加以使用。。例如,
http://www.springframework.org/index.html对应的视图文件名是index。
Spring提供了MultiActionController来将多个请求处理方法合并在一个控制器里,这样可以把相关功能组合在一起。
(如果你很熟悉Struts,会发现这与Struts的位于DispatchAction很像)
MultiActionControllerorg.springframework.web.mvc.multiaction包中,它可以定义页面请求到控制器方法名的映射,
然后在处理相应请求时调用该方法。当你有很多比较小的且相关的功能时使用MultiActionController很方便,这样就不必为每个小功能创建
一个单独的Controller了。但是一般来说MultiActionController不适合处理复杂逻辑,或者完全不相关
的功能,这时应该坚持使用标准方法,当在一个控制器存在大量公共的行为,但是有多个调用入口时,使用MultiActionController就特别方便。
MultiActionController有两种使用方式:一是创建MultiActionController的子类,并指定将被
MethodNameResolver解析的方法(这种情况下不需要这个delegate参数);二是定义一个委托对象,
MethodNameResolver解析出目标方法后将调用该对象的相应方法。这种情况下需要定义MultiActionController
的实例并将委托对象作为协作者注入(可通过构造参数或者setDelegate方法)。
MultiActionController需要一种策略,使其可以通过解析请求信息来获得要调用的方法。这个解析策略由
MethodNameResolver接口定义。MultiActionController提供了'methodNameResolver'
属性使得你可以注入需要的MethodNameResolver。在自己的MultiActionController(或者前面说的委托对象)
上定义的请求处理方法必须符合如下签名:
// 'anyMeaningfulName'指任意方法名
public [ModelAndView | Map | void] anyMeaningfulName(HttpServletRequest, HttpServletResponse [,HttpSession] [,AnyObject])
上述方法的详细信息可参考
MultiActionController类 Javadoc。如果打算使用MultiActionController,那最好看看它的Javadoc。不过,下面提供了
一些关于合法的请求处理方法的基本例子。
标准格式(跟Controller接口定义的一样)。
public ModelAndView displayCatalog(HttpServletRequest, HttpServletResponse)
下面这个方法接收Login参数,该参数中包含从请求中抽取出来的信息。
public ModelAndView login(HttpServletRequest, HttpServletResponse, Login)
下面这个方法要求请求中已经存在合法的session对象。
public ModelAndView viewCart(HttpServletRequest, HttpServletResponse, HttpSession)
下面这个方法接受一个Product参数,这个参数包含从请求中抽取出来的信息,并且要求请求中已经存在一个
合法的session对象。注意参数的顺序很重要:session必须是第三个参数,而绑定参数必须是final的,并位于session之后。
public ModelAndView updateCart(HttpServletRequest, HttpServletResponse, HttpSession, Product)
下面这个方法声明返回void类型,这说明它会直接写response。
public void home(HttpServletRequest, HttpServletResponse)
下面这个方法返回Map,表明视图解析器应该从请求中抽取视图名,而返回数据将被放入model
(参考第 13.11 节 “惯例优先原则(convention over configuration)”)。
public Map list(HttpServletRequest, HttpServletResponse)
MethodNameResolver负责从请求中解析出需要调用的方法名称。Spring本身已经提供了一系列
MethodNameResolver的实现,当然也可以编写自己的实现。注意,如果没有明确注入自己的实现,Spring默认使用
InternalPathMethodNameResolver。
InternalPathMethodNameResolver -从请求路径中获取文件名作为方法名
比如,http://www.sf.net/testing.view的请求会调用testing(HttpServletRequest,HttpServletResponse)方法。
ParameterMethodNameResolver - 解析请求参数,并将它作为方法名。
比如,对应http://www.sf.net/index.view?method=testIt的请求,会调用
testIt(HttpServletRequest, HttpServletResponse)方法)。使用paramName属性定义要使用的请求参数名称。
PropertiesMethodNameResolver - 使用用户自定义的属性(Properties)对象,将请求的URL映射到方法名。比如,当属性中包含
/index/welcome.html=doIt时,对/index/welcome.html 的请求会调用
doIt(HttpServletRequest, HttpServletResponse)方法。 PropertiesMethodNameResolver内部使用了
Spring的PathMatcher,所以支持路径通配符,比如上边那个URL写成welcom?.html也是可以的。
可以声明自己的方法来处理请求处理过程中产生的Exceptions。该方法的签名与请求处理方法的签名类似:第一个参数必须是
HttpServletRequest,第二个参数必须是HttpServletResponse。不过与请求处理
方法不同的是,该方法的名字可以任意,具体匹配策略由该方法的第三个参数(参数类型必须是一种Exception)决定。Spring根据最接近的
异常类型进行匹配。下面是一个这种异常处理方法签名的例子:
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()));
}
}
当使用上述的委托方式时,我们需要配置PropertiesMethodNameResolver,来完成与我们定义的方法的任意数量的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
(要了解更多关于控制器的信息,请参阅第 13.3 节 “控制器”)。
对ex目录中所有以view开始,以.html
结尾的请求都会被传递给helpController。
同样的,我们也为editAccountFormController定义了两个映射。
Spring的处理器映射支持拦截器。当你想要为某些请求提供特殊功能时,例如对用户进行身份认证,这就非常有用。
处理器映射中的拦截器必须实现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">
<value>
*.view=someController</value>
</property>
</bean>
在上面这个例子中,所有对*.view资源的请求,只要包含参数siteLanguage,
都会改变本地化信息。比如下面这个请求http://www.sf.net/home.view?siteLanguage=nl会将网站语言修改为荷兰语。