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
接口只定义每个控制器需要的通用的功能
- 处理动作请求,处理显示请求,返回模型和视图。
当然,仅一个Controller
是不够的。为了提供基本的功能,所有的Spring Portlet
Controller
从
AbstractController
继承,后者可以访问Spring
的ApplicationContext
和控制缓存。
表 16.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" class="samples.SampleController"> <property name="cacheSeconds" value="120"/> </bean>
为了使得一个简单的控制器工作,你只需要类似上面的类和在web应用context里的声明, 并且再设置一下处理器映射 (见 第 16.5 节 “处理器映射”)。
尽管你能够继承AbstractController
,
Spring Portlet MVC提供了不少具体的实现,它们提供了许多在简单MVC应用里
常用的功能。
ParameterizableViewController
基本上
和上面的例子类似,除了你能指定web应用context返回的视图的名字。
(不需要写死视图名)。
PortletModeNameViewController
把当前的
Portlet的状态作为视图名,如果Portlet在View模式
(比如:PortletMode.VIEW
),那“View”就是视图名。
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以及示例。
除了开发新的控制器,我们可以重用现有的portlet并且在
DispatcherPortlet
把请求映射指向它们。通过
PortletWrappingController
,你能实例化一个
现有的Portlet
来作
Controller
,如下所示:
<bean id="wrappingController" class="org.springframework.web.portlet.mvc.PortletWrappingController"> <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
。