第25章 认识更多Spring MVC家族成员
25.2 Handler与HandlerAdaptor
25.2.1 问题的起源
最初为了降低理解的难度,我们说,HandlerMapping将会通过HandlerExecutionChain返回一个Controller用于具体Web请求的处理。现在我们要进一步澄清事实: HandlerExecutionChain中所返回的用于处理Web请求的处理对象,可以不只是Controller一种类型。在Spring MVC中,任何可以用于Web请求处理的处理对象统称为Handler。 Controller是Handler的一种特殊类型。 HandlerMapping通过HandlerExecutionChain所返回的是一个Object类型的Handler对象,而并没限定说只能是Controller类型。 所以,一般意义上讲,任何类型的Handler都可以在Spring MVC中使用,比如Struts的Action和WebWork的Action等,只要它们是用于处理Web请求的处理对象就行。
不过,对于DispatcherServlet来说,这就有点儿问题了,它如何来判断我们到底使用的是什么类型的Handler,又如何决定调用Handler对象的哪个方法来处理Web请求呢?显然,在DispatcherServlet直接硬编码if- else来枚举每一种可能的Handler类型是不具任何扩展性的。 为了能够以统一的方式调用各种类型的Handler,DispatcherServlet将不同Handler的调用职责转交给了一个称为HandlerAdaptor的角色。
org.springframework.web.servlet.HandlerAdapter接口的定义如下:
public interface HandlerAdapter {
boolean supports(Object handler);
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
long getLastModified(HttpServletRequest request, Object handler);
}
实际上,人如其名。哦,不对!是”接口”如其名。HandlerAdaptor将作为一个适配器,屏蔽不同Handler类型给DispatcherServlet所造成的“困扰”(所谓AdaptorPattern应该也就是为了这种类似的目的吧)。HandlerAdapter得以成为DispatcherServlet和不同Handler的“中间人”,要归功于它的两个主要方法,即supports(..)
和handle(..)
。至于getLastModified(..)
方法,它的主要目的只是为返回给客户端的LastModified这个HTTP头提供相应的时间值。如果我们不想支持该功能,直接返回-1即可。DispatcherServlet从HandlerMapping获得一个Handler之后,将询问HandlerAdaptor的supports(..)
方法,以便了解
当前HandlerAdaptor是否支持HandlerMapping刚刚返回的Handler类型的调用。
如果supports(..)
返回true,Dispatcherservlet则调用HandlerAdaptor的handle(..)
方法,同时将刚才的Handler作为参数传入。方法执行后将返回ModelAndView,之后的工作就由ViewResolver接手了。
现在,DispatcherServlet只需要面对HandlerAdaptor提供的统一接口,而不需要面对纷繁复杂的Handler类型。支持新的Handler类型,意味着只需要为DispatcherServlet提供新的HandlerAdaptor实现即可。 DispatcherServlet借助多个HandlerAdaptor,调用当前HandlerMapping所返回的Handler来处理Web请求的逻辑, 如下方代码清单(伪代码形式)所示。
Object handler = <HandlerMappingt对象实例>.getHandler(request).getHandler();
HandlerAdaptor handlerAdaptorToUse = null;
HandlerAdaptor[] handlerAdaptors = ...;
for(HandlerAdaptor ha : handlerAdaptors) {
if(ha.supports(handler) {
handlerAdaptorToUse = ha;
break;
}
if(handlerAdaptorToUse == null)
throw exception for no handlerAdaptor found to use.
ModelAndView mav = handlerAdaptorToUse.handle(request, response, handler);
...
无论我们想在Spring MVC中使用什么类型的Handler,只要同时为DispatcherServlet提供对应该Handler的HandlerAdaptor实现就成,DispatcherServlet无须任何变动。我想,HandlerAdaptor存在的原因我们已经搞清楚了,但还有一些问题不明,比如:
-
如果Controller只是一种特殊类型的Handler,那么Spring MVC是否还提供了其他可用的Handler类型呢?如果要提供我们自己的Handler类型又需要考虑哪些事情呢?
-
如何实现一个具体的HandlerAdaptor?SpringMVC有提供现成的实现吗?
-
如果想使用自定义的Handler,并且提供了对应的HandlerAdaptor实现,要通过什么方式告知DispatcherServlet来使用它们?
我想只有解开以上问题的答案才能帮助我们更深刻地理解Handler与HandlerAdaptor之间的关系。
25.2.2 深入了解Handler
到目前为止,我们使用最多的Handier就是Controller。不过,如果Controller不合我们的口味的话,我们也可以使用Spring MVC提供的其他类型的Handler,甚至于自定义Handler类型。
1. 可用的Handler类型
除了Controller,SpringMVC还提供了一种与WebWork的Action功能类似的Handler实现,即 org.springframework.web.servlet.mvc.throwaway.ThrowawayController 。ThrowawayController接口只定义了一个方法,如下所示:
public interface ThrowawayController {
ModelAndView execute() throws Exception;
}
ThrowawayController的奧秘倒是不在这个处理方法上。 使用ThrowawayController类型的Handler的最大优势是,不需要依赖任何Servlet API,并且能够为它们定义状态,这使得ThrowawayController具有良好的可测试性。 下方代码清单给出的是一个典型的ThrowawayController实现。
public class SuchASimpleThrowawayController implements ThrowawayController {
private String parameterl;
private int parameter2;
// 其他必要的属性定义
public ModelAndView execute() throws Exception {
// MockService.doSthWith(parameter1, parameter2..)
ModelAndView mav = new ModelAndView();
return mav;
}
public String getParameter1() {
return parameter1;
}
public void setParameter1(String parameterl) {
this.parameter1 = parameter1;
}
public int getParameter2() {
return parameter2;
}
public void setParameter2(int parameter2) {
this.parameter2 = parameter2;
}
//getter和setter等方法定义
}
可以看到,ThrowawayController既提供处理逻辑的实现,又提供处理逻辑所需要的数据声明;框架类负责使用Servlet API提供到ThrowawayController所声明的数据的绑定;ThrowawayController实现类只需要使用绑定后的数据即可,根本不需要关心Servlet API是什么东西。
ThrowawayController不同于Controller,它拥有状态(这些状态值将由框架提供数据绑定),所以,添加到WebApplicationContext使用的ThrowawayController需要使用prototype类型的scope,而且,顾名思义ThrowawayController执行后将被丢弃,下次有新的Web请求需要同一类型的ThrowawayController处理,将获得一个新的实例。将SuchASimpleThrowawayController添加到容器后的bean定义,如下所示:
<bean id="sasThrowawayController" class="..SuchASimpleThrowawayController" scope="prototype"/>
在Spring 2.5中,ThrowawayController已经是不推荐使用(deprecated)状态,预计将在Spring3.0中删除,取而代之的是使用基于注解的Handler类型。如果你能够升级到Spring2.5,那么还是选用基于注解的Handler吧!
2. 自定义Handler
可以说,自定义Handler对于Handler类型来说并没任何限制,任何我们喜欢的形式都可以。如果不喜欢Controller,也不喜欢ThrowawayController,那么可以定义自己的MyHandler,甚至不需要强制Handler实现任何接口,仅是一个简单的POJO对象,只要能有办法知道该类就是用于Web请求处理的Handler类就行,比如用注解标注一下,然后通过反射机制就能获知那些对象是用于Web请求处理的Handler,如下所示:
@Handler
public class AnyType {
}
虽说对Handler自身没有任何限制,但是要让我们的Handler登上“历史舞台”发挥它的作用,却需要有能够给予帮助的“左膀右臂”, 为我们的Handler提供必要的HandlerMapping和HandlerAdaptor这才是真正让Handler自身没有任何限制的原因所在。
HandlerMapping负责查找相应的Handler以处理Web请求。 要想使用Handler,首先需要提供一个能够识别该Handler的HandlerMapping实现。 比如,无论是BeanNaimeUrlHandlerMapping还是SimpleUrlHandlerMapping,它们都可以获取并返回Controller类型或者ThrowawayController类型的Handler。如果可以通过BeanNameUrlHandlerMapping或者SimpleUrlHandlerMapping告知DispatcherServlet我们的Handler存在的话,那还好。否则,我们就不得不提供一个能够识别我们自己Handler类型的HandlerMapping。Spring 2.5就提供了特定的DefaultAnnotationHandlerMapping,处理新提供的基于注解的Handler的查找。
现在HandlerMapping返回了我们自定义的Handler,但DispatcherServlet本身显然是不管我们的Handler到底是何方人物的。 为了让我们的Handler得以被DispatcherServlet所“青睐”,我们不得不提供一个HandlerAdaptor。 实际上,并非只有我们的自定义Handler要“受此礼遇”,所有Spring MVC框架内的Handler都提供有相对应的HandlerAdaptor实现,如下所述。
-
Controller将org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapterlFhHandlerAdaptor作为其HandlerAdaptor。
-
org.springframework.web.servlet.mvc.throwaway.ThrowawayControllerHandlerAdapter对应ThrowawayController的HandlerAdaptor实现类。
-
Spring 2.5新添加的基于注解的Handler由 org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter作为其HandlerAdaptor。
这是一个“传统”,我们也得遵守。不过话又说回来了,保持框架设计的统一性确实非常重要。说了这么多,你对HandlerAdaptor可能还是没有一个感性的认识。别急,马上为你奉上。
25.2.3 近看HandlerAdaptor的奥秘
实际上, 为具体的Handler类型提供一个HandlerAdaptor实现类非常简单。主要工作只是调用这个HandlerAdaptor“认识”的Handler的Web请求处理方法,然后将处理结果转换为DispatcherServlet统一使用的ModelAndView就行。 我们不妨就以Controller的HandlerAdaptor为例,来看一下HandlerAdaptor实现到底长什么样子。下方代码清单给出的就是Controller对应的HandlerAdaptor实现代码。
public class SimpleControllerHandlerAdapter implements HandlerAdapter {
public boolean supports(Object handler) {
return(handler instanceof Controller);
}
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return((Controller)handler).handleRequest(request, response);
}
public long getLastModified(HttpServletRequest request, Object handler) {
if(handler instanceof LastModified) {
return((LastModified)handler).getLastModified(request);
}
return -1L;
}
}
SimpleControllerHandlerAdapter是Controller对应的HandlerAdaptor实现。supports(..)
方法决定了它只认识Controller一个人,所以,在handle(..)
方法中,直接将Object类型的Handler强制转型为Controller,然后调用其handleRequest(..)
即可。因为Controller的handleRequest(..)
方法可以返回已经组装好的ModelAndView,所以,就直接返回了。
有了SimpleControllerHandlerAdapter,我们是否可以给出更多的实现了呢?比如,假设我想在Spring MVC中使用Struts 1.x的Action作为Handler,那么是否可以提供一个类似下方代码清单所示的StrutsActionHandlerAdaptor呢?
public class StrutsActionHandlerAdaptor implements HandlerAdapter {
public boolean supports(Object handler) {
return(handler instanceof Action);
}
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Action action = (Action)handler;
ActionMapping actionMapping = getActionMapping();
ActionForm actionForm = createAndBindToActinForm(request, response);
ActionForward forward = action.execute(actionMapping, actionForm, request, response);
View view = constructViewAccordingToActionForward(forward);
return new Mode1AndView(view);
}
public long getLastModified(HttpServletRequest req, Object handler) {
return -1;
}
private ActionForm createAndBinaToActinForm(HttpServletRequest request, HttpServletResponse response) {
// ...
}
private ActionMapping getActionMapping() {
// ...
}
private View constructViewAccordingToActionForward(ActionForward forward) {
// ...
}
}
显然,只要能够给出对应的HandlerAdaptor实现,任何类型的Handler都可以纳入SpringMVC使用。
25.2.4 告知Handler与HandlerAdaptor的存在
我们已经有了要添加到Spring MVC框架的新的Handler类型,也给出了针对该Handler类型的HandlerAdaptor实现类,现在是让DispatcherServlet接纳它们的时候了!
首先,如果现有的HandlerMapping不足以“感知”到我们的Handler类型的话,那么我们需要提供一个能够“感知”我们Handler的HandlerMapping实现类,并将其注册到DispatcherServlet的WebApplicationContext中。如果现有的HandlerMapping实现可以“感知”到我们的Handler,那么将可以省去实现自定义HandlerMapping的工作,但依然需要将使用的HandlerMapping添加到DispatcherServlet的webApplicationContext中,除非我们使用默认的BeanNameUrlHandlerMapping。大部分情况下,只要我们提供的Handler在容器中的引用,能够明确指定给BeanNameUrlHandlerMapping或者SimpleUrlHandlerMapping等现有HandlerMapping实现类,都不需要自定义HandlerMapping实现。当然,在稍后详细介绍Spring 2.5新添加的基于注解的Handler实现的时候,我们将看到第一个为特定Handler提供的HandlerMapping实现。
其次,有了可以返回我们自定义的Handler的HandlerMapping之后,我们要为Dispatcherservlet提供能够调用该类型Handler的HandlerAdaptor实现。这同样是通过将HandlerAdaptor实现类添加到WebApplicationContext完成的,类似如下的bean定义:
<bean id="anyName" class="..MyHandlerAdaptor">
<!--必要的属性设置-->
</bean>
可以向DispatcherServlet的WebApplicationContext中添加多个HandlerAdaptor。DispatcherServlet将根据类型自动检测容器内可用的HandlerAdaptor实例。如果无法找到可用的HandlerAdaptor,DispatcherServlet将启用后备的几个默认使用的HandlerAdaptor实现,它们是:
-
org.springframework.web.servlet.mvC.HttpRequestHandlerAdapter;
-
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter;
-
org.springframework.web.servlet.mvc.throwaway.ThrowawayControllerHandlerAdapter;
-
org.springframework.web.servlet.mvC.annotation.AnnotationMethodHandlerAdapter。
也就是说,如果使用的Handler是现有Handler类型,那么无须在DispatcherServlet的WebApplicationContext中做任何配置,默认的HandlerAdaptor已经足够了。不过,如果需要添加这些HandlerAdaptor类型之外的HandlerAdaptor实现,并且我们依然希望同时使用这些默认HandlerAdaptor所支持的Handler的话,那就需要在添加我们的自定义HandlerAdaptor的基础.上,同时添加以上几种默认的HandlerAdaptor实现,如下方代码清单所示。
<bean id="anyName" class="..MyHandlerAdaptor">
<!--必要的属性设置-->
</bean>
<bean id=".." class="org.springframework.Web.servlet.mvc.HttpRequestHandlerAdapter">
<!--必要的属性设置-->
</bean>
<bean id=".." class="org.springframework.Web,servlet.mvc.SimpleControllerHandlerAdapter">
<!--必要的属性设置-->
</bean>
<bean id=".." class="org.springframework.Web.servlet.mvc.throwaway.ThrowawayControllerHandlerAdapter">
<!--必要的属性设置-->
</bean>
<bean id=".." class="org.springframework.Web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<!--必要的属性设置-->
</bean>
最后,不要忘了把每个Handler都添加到DispatcherServlet的WebApplicationContext中。当然,如果你的HandlerMapping不需要容器的支持也可以返回一个可用的Handler实例,那么这一步可以免掉。 不管怎么说,有了以上的工作,现在使用什么类型的Handler类来处理Web请求就可以随心所欲了。