17.4.控制器
17.4. 控制器
Portlet MVC里的控制器和Web MVC的很想相似,在两者之间移植代码应该很简单。
Portlet MVC控制器构架的基础是 org.springframework.web.portlet.mvc.Controller
接口,如下所示。
public interface Controller { /** * Process the render request and return a ModelAndView object which the * DispatcherPortlet will render. */ ModelAndView handleRenderRequest(RenderRequest request, RenderResponse response) throws Exception; /** * Process the action request. There is nothing to return. */ void handleActionRequest(ActionRequest request, ActionResponse response) throws Exception; }
如你所见,Portlet Controller
接口需要两个方法来处理Portlet 请求的两个阶段:动作请求和显示请求。动作阶段应该能够处理动作请求,显示阶段应该 能够处理显示请求,并返回合适的模型和视图。 尽管Controller
接口是抽象的,但Spring Portlet MVC 提供了很多包含了各种各样你需要的功能的控制器-它们中的大多数和Spring Web MVC里的控制器很类似。 Controller
接口只定义每个控制器需要的通用的功能 - 处理动作请求,处理显示请求,返回模型和视图。
17.4.1. AbstractController
和PortletContentGenerator
当然,仅一个Controller
是不够的。为了提供基本的功能,所有的Spring Portlet Controller
从 AbstractController
继承,后者可以访问Spring 的ApplicationContext
和控制缓存。
表 17.3. AbstractController
提供的功能
参数 | 解释 |
---|---|
requireSession | 表明当前的 Controller 是否需要session。 所有的控制器都能使用这个功能。如果这样的控制器收到请求时, session不存在,用户会收到 SessionRequiredException 。 |
synchronizeSession | 如果需要控制器在处理用户session时保持同步,使用 这个参数。更具体来说,扩展的控制器会覆盖handleRenderRequestInternal(..) 和handleActionRequestInternal(..) 方法,如果指定了这个参数, 这两个方法会在处理用户session时保持同步。 |
renderWhenMinimized | 如果需要在portlet最小化状态时,控制器也显示视图, 把这个参数设为true。这个参数缺省是false,所以portlet在最小化状态 时,不显示内容。 |
cacheSeconds | 在需要控制器覆盖当前portlet定义的缺省缓存失效时间时, 设置一个正的整数。这个参数缺省是-1 , 表示不改变缺省的缓存,把它设为0 ,就是 确保不缓存结果。 |
requireSession
和 cacheSeconds
属性是在 AbstractController
的父类 PortletContentGenerator
里声明的。为了完整性, 把它们列在这里。
在你自己的控制器里继承AbstractController
时 (不推荐这样做,因为已经有许多现成的控制器,它们可能有你需要的功能),仅需要覆盖 handleActionRequestInternal(ActionRequest, ActionResponse)
方法或 handleRenderRequestInternal(RenderRequest, RenderResponse)
方法(或两者都覆盖),实现逻辑, 并返回 ModelAndView
对象 (如果是 handleRenderRequestInternal
方法)。
handleActionRequestInternal(..)
和 handleRenderRequestInternal(..)
方法的缺省实现都会 抛出 PortletException
,这和JSR-168规范API里的 GenericPortlet
的行为是一致的。所以只要覆盖你的控制器 需要处理的方法。
下面简短的例子包含了一个类和一个在web应用context里的声明。
package samples; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; import org.springframework.web.portlet.mvc.AbstractController; import org.springframework.web.portlet.ModelAndView; public class SampleController extends AbstractController { public ModelAndView handleRenderRequestInternal( RenderRequest request, RenderResponse response) throws Exception { ModelAndView mav = new ModelAndView("foo"); mav.addObject("message", "Hello World!"); return mav; } } <bean id="sampleController"> <property name="cacheSeconds" value="120"/> </bean>
为了使得一个简单的控制器工作,你只需要类似上面的类和在web应用context里的声明, 并且再设置一下处理器映射 (见 第 17.5 节 “处理器映射”)。
17.4.2. 其它简单的控制器
尽管你能够继承AbstractController
, Spring Portlet MVC提供了不少具体的实现,它们提供了许多在简单MVC应用里 常用的功能。
ParameterizableViewController
基本上 和上面的例子类似,除了你能指定web应用context返回的视图的名字。 (不需要写死视图名)。
PortletModeNameViewController
把当前的 Portlet的状态作为视图名,如果Portlet在View模式 (比如:PortletMode.VIEW
),那“View”就是视图名。
17.4.3. Command控制器
Spring Portlet MVC提供了和Spring Web MVC完全一致的 command controllers层次结构,提供方法来与数据对象交互 并且动态地把参数从PortletRequest
绑定到数据对象上。数据对象不需要实现框架相关的接口,因而你可以 直接操作这些持久化对象。下面让我们查看Command控制器提供的功能, 来了解它们的使用:
AbstractCommandController
- Command控制器,可以用来创建自己的控制器,它能够将请求里的参数 绑定到指定的数据对象。这个类不提供表单功能,但它提供验证功能,并且 可以在控制器里指定如何处理带有请求参数的Command对象.AbstractFormController
- 提供表单提交支持的抽象控制器。你能够对表单进行建模,通过从控制器 里得到的Command对象来填充表单。在用户提交表单后,AbstractFormController
会绑定字段、进行验证, 然后把对象返回给控制器来做下一步的动作。支持的功能有:无效表单提交(重新 提交)、验正和通常的表单流程。你需要实现方法来决定表单的显示和成功时使用的 视图。如果你需要表单,但不想在应用context里指定用户看到的视图,使用这个 控制器。SimpleFormController
- 一个具体的AbstractFormController
, 对使用对应的command对象生成表单提供了更多的支持。SimpleFormController
可以让你在用户成功地提交 表单或其它状态时,指定command对象,表单的视图名以及页面对应的视图名。AbstractWizardFormController
– 具体的AbstractFormController
,它提交了向导式的接口 来编辑跨多个页面的command对象。支持多种用户动作:完成、取消或者页面变化,所有这些 都可以简便地在视图的请求参数里指定。
这些command控制器是非常强大的,为了有效地使用,需要对它们的原理有 细致的理解。在你开始使用它们前,务必仔细阅读它们层次结构的javadoc以及示例。
17.4.4. PortletWrappingController
除了开发新的控制器,我们可以重用现有的portlet并且在 DispatcherPortlet
把请求映射指向它们。通过 PortletWrappingController
,你能实例化一个 现有的Portlet
来作 Controller
,如下所示:
<bean id="wrappingController" > <property name="portletClass" value="sample.MyPortlet"/> <property name="portletName" value="my-portlet"/> <property name="initParameters"> <value> config=/WEB-INF/my-portlet-config.xml </value> </property> </bean>
这会很有价值,因为可以使用拦截器来对送向这些portlet的请求进行预处理和后处理。 而且也很方便,因为JSR-168没有提供对过滤机制的支持。比如,可以在一个MyFaces JSR Portlet外面加上Hibernate的 OpenSessionInViewInterceptor
。