16.5. 处理器映射

Spring Framework

16.5. 处理器映射

通过处理器映射,可以把进来的portlet请求对应到合适的处理器上。已经有一些 现成的处理器映射可以使用,比如PortletModeHandlerMapping。 但还是让我们先看一下HandlerMapping的一般概念。

注意,我们这里有意使用“处理器”来代替“控制器”。 DispatcherPortlet是设计用来和多种方式一起处理请求的, 而不仅仅是和Spring Portlet MVC自己的控制器。处理器是任意可以处理Portlet请求的对象。 控制器当然缺省是一种处理器。要将DispatcherPortlet 和一些其他的框架一起使用,只需要实现相应的HandlerAdapter就可以了。

HandlerMapping提供的基本功能是提供一个 HandlerExecutionChain,后者必须包含匹配进来请求的 的处理器,也可能包含需要应用到请求的处理器拦截器的列表。当一个请求进来时, DispatcherPortlet会把它交给处理器射映,让它来检查 请求并得到合适的HandlerExecutionChain。然后 DispatcherPortlet会执行处理器以及chain里的拦截器。这些 概念和Spring Web MVC中的完全一致。

可配置的处理器映射非常强大,它可以包含拦截器(在实际的处理前、后进行预处理或后处理 或两者都执行)。可以通过自定义一个HandlerMapping来加入许多功能。 想像一下,一个自定义的处理器映射,它不仅可以根据指定的portlet模式来选择处理器, 也可以根据请求相联系的session里的指定状态来选择。

在Spring Web MVC里,处理器映射通常是基于URL的。因为在Portlet里确实没有URL, 必须使用其它的机制来控制映射。最常见的两个是portlet模式和请求参数, 但在portlet请求里的任何对象都可以用在自定义的处理器映射中。

余下的章节会介绍在Spring Portlet MVC里最常见的三种处理器射映, 它们都继承AbstractHandlerMapping并且共享以下的属性:

  • interceptors: 需要使用的拦截器列表。 HandlerInterceptor第 16.5.4 节 “增加 HandlerInterceptors”有讨论。

  • defaultHandler: 在找不到匹配的处理器时,缺省的处理器。

  • order: Spring会按照order属性值 (见org.springframework.core.Ordered接口) 对context里的所有处理器映射进行排序,并且应用第一个匹配的处理器。

  • lazyInitHandlers: 用来Lazy初始化单例 处理器(prototype处理器是始终lazy初始化的)。缺省值是false。这个属性是在这三个具体处理器里直接实现。

16.5.1. PortletModeHandlerMapping

这是一个简单的处理器映射,它是基于当前的portlet模式(比如:'view', 'edit', 'help')。如下:

<bean id="portletModeHandlerMapping"
      class="org.springframework.web.portlet.handler.PortletModeHandlerMapping">
    <property name="portletModeMap">
        <map>
            <entry key="view" value-ref="viewHandler"/>
            <entry key="edit" value-ref="editHandler"/>
            <entry key="help" value-ref="helpHandler"/>
        </map>
    </property>
</bean>

16.5.2. ParameterHandlerMapping

如果需要在不改变portlet模式的情况下而在多个控制器间切换, 最简单的方法是把一个请求参数作为key来控制映射。

ParameterHandlerMapping使用一个特定的请求参数来控制映射。 这个参数的缺省名是'action',可以通过'parameterName'属性来改变。

这个映射的bean设置会是这样:

<bean id="parameterHandlerMapping"
      class="org.springframework.web.portlet.handler.ParameterHandlerMapping”>
    <property name="parameterMap">
        <map>
            <entry key="add" value-ref="addItemHandler"/>
            <entry key="edit" value-ref="editItemHandler"/>
            <entry key="delete" value-ref="deleteItemHandler"/>
        </map>
    </property>
</bean>

16.5.3. PortletModeParameterHandlerMapping

最强大的内置处理映射 PortletModeParameterHandlerMapping 结合了前两者的功能, 能够在每种portlet模式下进行不同的切换。

同样,参数的缺省名是“action”,但可以通过 parameterName 来修改。

缺省情况下,同样的参数值不能在两个不同的portlet模式下使用, 因为如果portlet自己改变了portlet模式,那么请求在映射中将不在有效。 把 allowDupParameters 属性设为true可以改变这种行为,但这种做法是不推荐的。

这个映射的bean设置会是这样:

<bean id="portletModeParameterHandlerMapping"
      class="org.springframework.web.portlet.handler.PortletModeParameterHandlerMapping">
    <property name="portletModeParameterMap">
        <map>
            <entry key="view"> <!-- 'view' portlet mode -->
                <map>
                    <entry key="add" value-ref="addItemHandler"/>
                    <entry key="edit" value-ref="editItemHandler"/>
                    <entry key="delete" value-ref="deleteItemHandler"/>
                </map>
            </entry>
            <entry key="edit"> <!-- 'edit' portlet mode -->
                <map>
                    <entry key="prefs" value-ref="prefsHandler"/>
                    <entry key="resetPrefs" value-ref="resetPrefsHandler"/>
                </map>
            </entry>
        </map>
    </property>
</bean>

这个映射可以在处理链中放在 PortletModeHandlerMapping 前面,它可以为每个模式以及全局提供缺省的映射。

16.5.4. 增加 HandlerInterceptors

Spring的处理器映射机制里有处理器拦截器的概念,在希望对于特定的请求 应用不同的功能时,它是非常有用。比如,检查用户名(principal)。同样,Spring Portlet MVC以Web MVC相同的方式实现了这些概念。

在处理器映射里的拦截器必须实现org.springframework.web.portlet 里的HandlerInterceptor接口。 和servlet的版本一样,这个接口定义了三个方法:一个在实际的处理器执行前被调用 (preHandle),一个在执行后被调用(postHandle) 还有一个是在请求完全结束时被调用(afterCompletion)。 这三个方法应该可以为各种前置和后置处理提供足够的灵活。

preHandle返回一个布尔值。可以使用这个方法来中断或者继续执行链的处理。 当返回true时,处理执行链会继续,当返回false时, DispatcherPortlet 假设这个拦截器已经处理请求(比如,显示了合适的视图)并且不需要继续执行其它的 拦截器和在执行链中实际的处理器。

postHandle只会在RenderRequest 中被调用。ActionRequestRenderRequest 都会调用preHandleafterCompletion方法。 如果希望只在其中的一种请求中执行你的代码,务必在处理前检查请求的类型。

16.5.5. HandlerInterceptorAdapter

和servlet包类似,portlet包里也有一个 HandlerInterceptor 的具体实现 HandlerInterceptorAdapter 。这个类所有方法都是空的, 所以可以继承它,实现一个或两个你所需要的方法。

16.5.6. ParameterMappingInterceptor

Portlet包也带一个名为ParameterMappingInterceptor 的具体拦截器,它可以和ParameterHandlerMapping 以及PortletModeParameterHandlerMapping一起使用。 这个拦截器可以把用来控制映射的参数从ActionRequest 带到随后的RenderRequest,这能够确保 RenderRequest映射到和 ActionRequest相同的处理器。这些都是在 preHandle方法里完成的,所以在你的处理器里仍然可以改变决定 RenderRequest映射的参数值。

注意这个拦截器会调用ActionResponsesetRenderParameter方法,这意味着在使用它的时候, 不能在处理器里调用sendRedirect。如果确实需要重定向, 可以手工地把映射参数向前传,或者另写一个拦截器来处理。