Spring Framework 中文文档

4.3.21.RELEASE

25. Portlet MVC Framework

25.1 简介


JSR-286 Java Portlet 规范

有关 portlet 开发的更多常规信息,请查看JSR-286 规格本身。


除了支持常规(servlet-based)Web 开发外,Spring 还支持 JSR-286 Portlet 开发。 Portlet MVC framework 尽可能地是 Web MVC framework 的镜像,并且还使用相同的底层视图抽象和 integration 技术。因此,在继续本章之前,请务必查看标题为第 22 章,Web MVC framework第 23 章,查看技术的章节。

请记住,虽然 Spring MVC 的概念在 Spring Portlet MVC 中是相同的,但是 JSR-286 portlet 的独特工作流程会产生一些显着的差异。

Portlet 工作流与 servlet 工作流不同的主要方式是对 portlet 的请求可以有两个不同的阶段:操作阶段和呈现阶段。操作阶段仅执行一次,并且是任何“后端”更改或操作发生的位置,例如在数据库中进行更改。然后,渲染阶段产生每次显示刷新时向用户显示的内容。这里的关键点是,对于单个整体请求,动作阶段仅执行一次,但渲染阶段可以多次执行。这提供(并要求)修改系统的持久 state 的活动与生成显示给用户的活动之间的清晰分离。


春天 Web 流

Spring Web Flow(SWF)旨在成为 web application 页面流的 management 的最佳解决方案。

SWF 在 Servlet 和 Portlet 环境中与现有框架(如 Spring MVC 和 JSF)集成。如果您有一个业务 process(或进程)可以从对话 model 而不是纯请求 model 中受益,那么 SWF 可能就是解决方案。

SWF 允许您将逻辑页面流捕获为可在不同情况下重用的 self-contained 模块,因此非常适合 building web application 模块,这些模块可引导用户完成驱动业务流程的受控导航。

有关 SWF 的更多信息,请参阅 Spring Web Flow 网站。


portlet 请求的双重阶段是 JSR-286 规范的真正优势之一。例如,动态搜索结果可以在显示屏上定期更新,而无需用户明确重新运行搜索。大多数其他 portlet MVC 框架试图完全隐藏开发人员的两个阶段,使其看起来尽可能像传统的 servlet 开发 - 我们认为这种方法消除了使用 portlet 的主要好处之一。因此,在整个 Spring Portlet MVC framework 中保留了两个阶段的分离。这种方法的主要表现形式是,MVC classes 的 servlet version 将有一个处理请求的方法,MVC classes 的 portlet version 将有两个处理请求的方法:一个用于操作阶段,一个用于处理请求用于渲染阶段。对于 example,AbstractController的 servlet version 具有handleRequestInternal(..)方法,AbstractController的 portlet version 具有handleActionRequestInternal(..)handleRenderRequestInternal(..)方法。

framework 是围绕DispatcherPortlet设计的,它使用可配置的处理程序映射和视图分辨率将请求分派给处理程序,就像 web framework 中的DispatcherServlet一样。也以相同的方式支持文件上载。

Portlet MVC 不支持 Locale 解析和主题解析 - 这些区域位于 portal/portlet 容器的范围内,并且不适合 Spring level。但是,Spring 中依赖于 locale 的所有机制(例如消息的国际化)仍将正常运行,因为DispatcherPortlet以与DispatcherServlet相同的方式公开当前的 locale。

25.1.1 控制器 - MVC 中的 C.

默认处理程序仍然是一个非常简单的Controller接口,只提供两种方法:

  • void handleActionRequest(request,response)

  • ModelAndView handleRenderRequest(request,response)

framework 还包括大多数相同的控制器 implementation 层次结构,例如AbstractControllerSimpleFormController等。数据 binding,命令 object 用法,模型处理和视图分辨率都与 servlet framework 中的相同。

25.1.2 Views - MVC 中的 V.

servlet framework 的所有视图呈现功能都直接通过名为ViewRendererServlet的特殊 bridge servlet 使用。通过使用此 servlet,portlet 请求将转换为 servlet 请求,并且可以使用整个普通 servlet 基础结构呈现视图。这意味着所有现有的渲染器,例如 JSP,Velocity 等,仍然可以在 portlet 中使用。

25.1.3 Web-scoped beans

Spring Portlet MVC 支持 beans,其生命周期范围限定为当前 HTTP 请求或 HTTP Session(normal 和 global)。这不是 Spring Portlet MVC 本身的特定 feature,而是 Spring Portlet MVC 使用的WebApplicationContext container(s)。这些 bean 范围在第 7.5.4 节,“请求,session,global session,application 和 WebSocket 范围”中有详细描述

25.2 DispatcherPortlet

Portlet MVC 是一个 request-driven web MVC framework,围绕一个 portlet 设计,该 portlet 将请求分派给控制器并提供其他功能,以促进 portlet applications 的开发。 Spring 的DispatcherPortlet然而,不仅如此。它与 Spring ApplicationContext完全集成,并允许您使用 Spring 所具有的所有其他 feature。

与普通的 portlet 一样,在 web application 的portlet.xml文件中声明:

<portlet>
    <portlet-name>sample</portlet-name>
    <portlet-class>org.springframework.web.portlet.DispatcherPortlet</portlet-class>
    <supports>
        <mime-type>text/html</mime-type>
        <portlet-mode>view</portlet-mode>
    </supports>
    <portlet-info>
        <title>Sample Portlet</title>
    </portlet-info>
</portlet>

现在需要配置DispatcherPortlet

在 Portlet MVC framework 中,每个DispatcherPortlet都有自己的WebApplicationContext,它继承了 Root WebApplicationContext中已定义的所有 beans。可以在 portlet-specific 范围中覆盖这些继承的 beans,并且可以在给定的 portlet 实例的本地定义新的 scope-specific beans。

DispatcherPortlet初始化时,framework 将在 web application 的WEB-INF目录中查找名为[portlet-name]-portlet.xml的文件,并创建在那里定义的 beans(覆盖在 global 范围内使用相同 name 定义的任何 beans 的定义)。

可以通过 portlet 初始化参数修改DispatcherPortlet使用的配置位置(有关详细信息,请参见下文)。

Spring DispatcherPortlet有一些它使用的特殊 beans,在 order 中能够处理请求并呈现适当的视图。这些 beans 包含在 Spring framework 中,可以在WebApplicationContext中配置,就像配置任何其他 bean 一样。下面将更详细地描述这些 beans 中的每一个。现在,我们只是提到它们,只是为了让你知道它们存在并让我们继续谈论DispatcherPortlet。对于大多数 beans,提供了默认值,因此您不必担心配置它们。

表格 1_.WebApplicationContext 中的特殊 beans

表达说明
handler mapping(s)(第 25.5 节,“处理程序映射”)一个 pre-and post-processors 和控制器的列表,如果它们匹配某些条件(例如用控制器指定的匹配的 portlet 模式),它们将被执行
controller(s)(第 25.4 节,“控制器”)beans 提供实际功能(或至少访问功能)作为 MVC 三元组的一部分
查看解析器(第 25.6 节,“查看和解决它们”)能够解析视图名称以查看定义
multipart 解析器(第 25.7 节,“多部分(文件上传)支持”)提供从 HTML 表单处理文件上传的功能
handler exception 解析器(第 25.8 节,“处理 exceptions”)提供 map exceptions 视图的功能或实现其他更复杂的 exception 处理 code

DispatcherPortlet被设置为使用并且针对该特定DispatcherPortlet的请求进入时,它开始处理该请求。下面的列表描述了如果由DispatcherPortlet处理请求所经过的完整 process:

  • PortletRequest.getLocale()返回的 locale 绑定到请求,以便让 process 中的元素解析处理请求时使用的 locale(渲染视图,准备数据,etc.))。

  • 如果指定了 multipart 解析程序并且这是ActionRequest,则检查请求的多部分,如果找到它们,则将其包装在MultipartActionRequest中,以便 process 中的其他元素进一步处理。 (有关 multipart 处理的更多信息,请参见第 25.7 节,“多部分(文件上传)支持”)。

  • 搜索适当的处理程序。如果找到了一个处理程序,那么与处理程序相关联的执行链(pre-processors,post-processors,controllers)将在 order 中执行以准备 model。

  • 如果返回 model,则使用已使用WebApplicationContext配置的视图解析器呈现视图。如果没有返回 model(这可能是由于 pre-或 post-processor 拦截请求,对于 example,出于安全原因),不会呈现任何视图,因为该请求可能已经被满足。

在处理请求期间抛出的异常被WebApplicationContext中声明的任何处理程序 exception 解析程序拾取。使用这些 exception 解析器,您可以定义自定义行为,以防抛出这样的 exceptions。

您可以通过在portlet.xml文件或 portlet init-parameters 中添加 context 参数来自定义 Spring 的DispatcherPortlet。可能性如下所列。

表格 1_.DispatcherPortlet 初始化参数

参数说明
contextClassClass 实现WebApplicationContext,它将用于实例化此 portlet 使用的 context。如果未指定此参数,则将使用XmlPortletApplicationContext
contextConfigLocationString 传递给 context 实例(由contextClass指定)以指示可以找到 context(s 的位置.String 可能被拆分为多个 Strings(使用逗号作为分隔符)以支持多个上下文(在多个 context 位置的情况下,对于定义两次的 beans,最新优先)。
namespaceWebApplicationContext的命名空间。默认为[portlet-name]-portlet
viewRendererUrlDispatcherPortlet可以访问ViewRendererServlet实例的 URL(请参阅第 25.3 节,“ViewRendererServlet”)。

25.3 ViewRendererServlet

Portlet MVC 中的呈现 process 比 Web MVC 中的复杂一点。在 order 中重用 Spring Web MVC 中的所有查看技术,我们必须将PortletRequest/PortletResponse转换为HttpServletRequest/HttpServletResponse,然后调用Viewrender方法。为此,DispatcherPortlet使用了一个特殊的 servlet,它就是为了这个目的而存在的:ViewRendererServlet

DispatcherPortlet渲染工作的 order 中,您必须在 web application 的web.xml文件中声明ViewRendererServlet的实例,如下所示:

<servlet>
    <servlet-name>ViewRendererServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.ViewRendererServlet</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>ViewRendererServlet</servlet-name>
    <url-pattern>/WEB-INF/servlet/view</url-pattern>
</servlet-mapping>

要执行实际渲染,DispatcherPortlet执行以下操作:

  • WebApplicationContext绑定到请求作为DispatcherServlet使用的相同WEB_APPLICATION_CONTEXT_ATTRIBUTE key 下的属性。

  • ModelView objects 绑定到请求以使它们可用于ViewRendererServlet

  • 构造PortletRequestDispatcher并使用映射到ViewRendererServlet/WEB- INF/servlet/view URL 执行include

然后ViewRendererServlet能够使用适当的 arguments 调用View上的render方法。

可以使用DispatcherPortlet's viewRendererUrl``+111+`的实际 URL。

25.4 控制器

Portlet MVC 中的控制器与 Web MVC 控制器非常相似,将 code 从一个移植到另一个应该很简单。

Portlet MVC 控制器 architecture 的基础是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接口只定义了每个控制器所需的最常见的功能:处理动作请求,处理渲染请求,以及返回 model 和视图。

25.4.1 AbstractController 和 PortletContentGenerator

当然,只是一个Controller接口是不够的。为了提供基本的基础结构,所有 Spring Portlet MVC 的Controller都继承自AbstractController,一个 class 提供对 Spring 的ApplicationContext的访问并控制缓存。

表格 1_.AbstractController 提供的 Features

参数说明
requireSession指示此Controller是否需要 session 来执行其工作。此 feature 提供给所有控制器。如果在这样的控制器接收到请求时不存在 session,则使用SessionRequiredException通知用户。
synchronizeSession如果您希望此控制器的处理在用户的 session 上同步,请使用此选项。更具体地说,扩展控制器将覆盖handleRenderRequestInternal(..)handleActionRequestInternal(..)方法,如果指定此变量,这些方法将在用户的 session 上同步。
renderWhenMinimized如果希望控制器在 portlet 处于最小化 state 时实际呈现视图,请将其设置为 true。默认情况下,将其设置为 false,以使最小化 state 中的 portlet 不显示任何内容。
cacheSeconds如果希望控制器覆盖为 portlet 定义的默认缓存过期,请在此处指定正__gerger。默认情况下,它设置为-1,不会更改默认缓存。将其设置为0将确保结果永远不会被缓存。

requireSessioncacheSeconds properties 在PortletContentGenerator class 上声明,它是AbstractController的超类,但是为了完整性而包括在这里。

当使用AbstractController作为控制器的 base class 时(不建议这样做,因为有很多其他控制器可能已经为你做了 job),你只需要覆盖handleActionRequestInternal(ActionRequest, ActionResponse)方法或handleRenderRequestInternal(RenderRequest, RenderResponse)方法(或两者) ,实现你的逻辑,并_返回一个ModelAndView object(在handleRenderRequestInternal的情况下)。

handleActionRequestInternal(..)handleRenderRequestInternal(..)的默认_implement 将抛出PortletException。这与 JSR-168 规范 API 中GenericPortlet的行为一致。因此,您只需要覆盖控制器要处理的方法。

这是一个简短的 example,由_cb 和 web application 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) {
        ModelAndView mav = new ModelAndView("foo");
        mav.addObject("message", "Hello World!");
        return mav;
    }

}
<bean id="sampleController" class="samples.SampleController">
    <property name="cacheSeconds" value="120"/>
</bean>

除了设置处理程序映射(请参阅第 25.5 节,“处理程序映射”)以使这个非常简单的控制器工作之外,上面的 class 和 web application context 中的声明是您所需要的。

25.4.2 其他简单的控制器

虽然你可以扩展AbstractController,但是 Spring Portlet MVC 提供了许多具体的 implementations,它们提供了简单的 MVC applications 中常用的功能。

ParameterizableViewController基本上与上面的 example 相同,除了你可以在 web application context 中指定它将 return 的 view name(不需要 hard-code 视图 name)。

PortletModeNameViewController使用 portlet 的当前模式作为 view name。因此,如果您的 portlet 处于 View 模式(i.e.PortletMode.VIEW),那么它使用“view”作为 view name。

25.4.3 命令控制器

Spring Portlet MVC 与 Spring Web MVC 具有完全相同的命令控制器层次结构。它们提供了一种与数据 objects 交互的方法,并动态地将参数从PortletRequest绑定到指定的数据 object。您的数据 objects 不必实现 framework-specific 接口,因此您可以根据需要直接操作持久性 objects。让我们来看看哪些命令控制器可用,以便概述你可以用它们做什么:

  • AbstractCommandController - 一个命令控制器,可用于创建自己的命令控制器,能够将请求参数绑定到您指定的数据对象。此 class 不提供表单功能,但它提供验证 features,并允许您在控制器本身中指定如何处理已填充了请求中的参数的命令 object。

  • AbstractFormController - 提供表单提交支持的抽象控制器。使用此控制器,您可以 model 表单并使用您在控制器中检索的命令 object 填充它们。用户填写表单后,AbstractFormController绑定字段,验证并将 object 交给控制器以采取适当的操作。支持的 features 包括:无效的表单提交(重新提交),验证和普通表单工作流。您可以实现方法来确定哪些视图用于表单呈现和成功。如果需要表单,请使用此控制器,但不希望指定要在 application context 中向用户显示的视图。

  • SimpleFormController - 具体AbstractFormController,在使用相应的命令 object 创建表单时提供更多支持。 SimpleFormController允许您指定命令 object,表单的视图名称,表单提交成功时要向用户显示的页面的视图名称等等。

  • AbstractWizardFormController - 具体AbstractFormController,提供 wizard-style 接口,用于跨多个显示页面编辑命令 object 的内容。支持多个用户操作:完成,取消或页面更改,所有这些操作都可以在视图的请求参数中轻松指定。

这些命令控制器非常强大,但它们确实需要详细了解它们如何在 order 中有效地使用它们。仔细查看整个层次结构的 javadoc,然后在开始使用它们之前查看一些 sample implementations。

25.4.4 PortletWrappingController

而不是 developing 新控制器,可以从DispatcherPortlet使用现有的 portlet 和 map 请求。使用PortletWrappingController,您可以将现有Portlet实例化为Controller,如下所示:

<bean id="myPortlet" 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>

这可能非常有价值,因为您可以使用拦截器将 pre-process 和 post-process 请求发送到这些 portlet。由于 JSR-286 不支持任何类型的过滤机制,因此非常方便。对于 example,这可以用于围绕 MyFaces JSF Portlet 包装 Hibernate OpenSessionInViewInterceptor

25.5 处理程序映射

使用处理程序映射,您可以将传入的 portlet 请求映射到适当的处理程序。你可以使用一些处理器映射,例如,PortletModeHandlerMapping,但让我们首先检查一下HandlerMapping的一般概念。

注意:我们故意在这里使用术语“处理程序”而不是“控制器”。 DispatcherPortlet旨在用于处理请求的其他方式,而不仅仅是 Spring Portlet MVC 自己的控制器。 Handler 是任何可以处理 portlet 请求的 Object。控制器是处理程序的一个例子,它们当然是默认的。要使用DispatcherPortlet的其他 framework,只需要HandlerAdapter的相应 implementation。

basic HandlerMapping提供的功能是传递HandlerExecutionChain,它必须包含与传入请求匹配的处理程序,还可以包含应用于请求的处理程序拦截器列表。当请求进入时,DispatcherPortlet会将其交给处理程序映射,让它检查请求并提供适当的HandlerExecutionChain。然后DispatcherPortlet将执行链中的处理程序和拦截器(如果有的话)。这些概念与 Spring Web MVC 中的概念完全相同。

可配置处理程序映射的概念可以选择性地包含拦截器(在执行实际处理程序之前或之后执行,或两者都执行)非常强大。许多支持功能可以构建到自定义HandlerMapping中。考虑一个自定义处理程序映射,它不仅根据进入请求的 portlet 模式而且还根据与请求关联的 session 的特定 state 来选择处理程序。

在 Spring Web MVC 中,处理程序映射通常基于 URL。由于 Portlet 中实际上没有 URL 这样的东西,我们必须使用其他机制来控制映射。最常见的两个是 portlet 模式和请求参数,但 portlet 请求可用的任何内容都可以在自定义处理程序映射中使用。

本节的 rest 描述了三个 Spring Portlet MVC 最常用的处理程序映射。它们都扩展AbstractHandlerMapping并共享以下 properties:

  • interceptors:要使用的拦截器列表。 HandlerInterceptor第 25.5.4 节,“添加 HandlerInterceptors”中讨论。

  • defaultHandler:当此处理程序映射不会导致匹配处理程序时要使用的默认处理程序。

  • order:基于 order property 的 value(参见org.springframework.core.Ordered接口),Spring 将对 context 中可用的所有处理程序映射进行排序,并应用第一个匹配的处理程序。

  • lazyInitHandlers:允许 singleton 处理程序的延迟初始化(原型处理程序总是被懒惰地初始化)。默认 value 是 false。这个 property 直接在三个具体的处理程序中实现。

25.5.1 PortletModeHandlerMapping

这是一个简单的处理程序映射,它根据 portlet 的当前模式(e.g. 'view','edit','help')映射 maps 传入请求。一个例子:

<bean 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>

25.5.2 ParameterHandlerMapping

如果我们需要在不改变 portlet 模式的情况下导航到多个控制器,最简单的方法是使用一个请求参数作为 key 来控制映射。

ParameterHandlerMapping使用特定请求参数的 value 来控制映射。参数的默认 name 是'action',但可以使用'parameterName' property 进行更改。

此映射的 bean configuration 将如下所示:

<bean 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>

25.5.3 PortletModeParameterHandlerMapping

最强大的 built-in 处理程序映射,PortletModeParameterHandlerMapping结合了前两个的功能,允许在每个 portlet 模式下进行不同的导航。

同样,参数的默认 name 是“action”,但可以使用parameterName property 进行更改。

默认情况下,相同的参数 value 可能不会在两种不同的 portlet 模式中使用。这样,如果门户网站本身更改了 portlet 模式,则该请求将不再在映射中有效。通过将allowDupParameters property 设置为 true 可以更改此行为。但是,不建议这样做。

此映射的 bean configuration 将如下所示:

<bean 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之前链接,然后可以为每个模式提供默认值,也可以提供整体默认值。

25.5.4 添加 HandlerInterceptors

Spring 的处理程序映射机制有一个处理程序拦截器的概念,当您想要将特定功能应用于某些请求时,这非常有用,例如,检查主体。再次 Spring Portlet MVC 以与 Web MVC 相同的方式实现这些概念。

位于处理程序映射中的拦截器必须从org.springframework.web.portlet包中实现HandlerInterceptor。就像 servlet version 一样,这个接口定义了三个方法:一个将在执行实际处理程序之前调用(preHandle),一个将在执行处理程序之后调用(postHandle),另一个在完成后调用。请求已完成(afterCompletion)。这三种方法应该提供足够的灵活性来进行各种预处理和后处理。

preHandle方法返回 boolean value。您可以使用此方法 break 或继续处理执行链。当此方法返回true时,处理程序执行链将_继续。当它返回false时,DispatcherPortlet假定拦截器本身已处理请求(并且,对于 example,呈现了适当的视图)并且不继续执行其他拦截器和执行链中的实际处理程序。

postHandle方法仅在RenderRequest上调用。在ActionRequestRenderRequest上都会调用preHandleafterCompletion方法。如果您需要在这些方法中仅针对一种类型的请求执行逻辑,请务必在处理之前检查它是什么类型的请求。

25.5.5 HandlerInterceptorAdapter

与 servlet 包一样,portlet 包具有HandlerInterceptor的具体_implement,称为HandlerInterceptorAdapter。这个 class 有所有方法的空版本,这样你就可以继承这个 class,只需要实现一两个方法就可以了。

25.5.6 ParameterMappingInterceptor

portlet 包还有一个名为ParameterMappingInterceptor的具体拦截器,它可以直接与ParameterHandlerMappingPortletModeParameterHandlerMapping一起使用。此拦截器将导致用于控制映射的参数从ActionRequest转发到后续RenderRequest。这将有助于确保RenderRequest映射到与ActionRequest相同的 Handler。这是在拦截器的preHandle方法中完成的,因此您仍然可以修改处理程序中的参数 value 以更改RenderRequest的映射位置。

请注意,此拦截器在ActionResponse上调用setRenderParameter,这意味着在使用此拦截器时,无法在处理程序中调用sendRedirect。如果您需要进行外部重定向,那么您将需要手动转发映射参数或编写不同的拦截器来为您处理此问题。

25.6 查看并解决它们

如前所述,Spring Portlet MVC 直接重用 Spring Web MVC 中的所有视图技术。这不仅包括各种View __mplementations 本身,还包括ViewResolver __mplementations。有关更多信息,请分别参阅第 23 章,查看技术第 22.5 节,“解析观点”

关于使用现有ViewViewResolver implementations 的一些项目值得一提:

  • 大多数门户网站都希望将 portlet 呈现为 HTML 片段。所以,像 JSP/JSTL,Velocity,FreeMarker 和 XSLT 这样的东西都有意义。但是,返回其他文档类型的视图在 portlet context 中不太可能有意义。

  • 在 portlet 中没有 HTTP 重定向(ActionResponsesendRedirect(..)方法不能用于保留在门户中)。因此,RedirectView和使用'redirect:'前缀将无法在 Portlet MVC 中正常工作。

  • 可以在 Portlet MVC 中使用'forward:'前缀。但是,请记住,由于您位于 portlet 中,因此当前 URL 没有 idea。这意味着您不能使用相对 URL 访问 web application 中的其他资源,并且您必须使用绝对 URL。

此外,对于 JSP 开发,新的 Spring Taglib 和新的 Spring Form Taglib 都在 portlet 视图中工作,其工作方式与它们在 servlet 视图中的工作方式完全相同。

25.7 Multipart(文件上传)支持

Spring Portlet MVC 有 built-in multipart 支持来处理 portlet applications 中的文件上传,就像 Web MVC 一样。 multipart 支持的设计是使用org.springframework.web.portlet.multipart包中定义的可插入PortletMultipartResolver objects 完成的。 Spring 提供PortletMultipartResolver用于Commons _FileUpload。如何支持上传 files 将在本节的 rest 中描述。

默认情况下,Spring Portlet MVC 不会进行 multipart 处理,因为一些开发人员希望自己处理多部分。您必须通过向 web application 的 context 添加 multipart 解析器来自己启用它。完成后,DispatcherPortlet将检查每个请求以查看它是否包含 multipart。如果未找到 multipart,则请求将按预期继续。但是,如果在请求中找到 multipart,则将使用 context 中声明的PortletMultipartResolver。之后,请求中的 multipart 属性将被视为任何其他属性。

任何已配置的PortletMultipartResolver bean 必须具有以下 id(或 name):“portletMultipartResolver”。如果您使用任何其他 name 定义了PortletMultipartResolver,那么DispatcherPortlet将找不到您的PortletMultipartResolver,因此没有 multipart 支持将生效。

25.7.1 使用 PortletMultipartResolver

以下 example 显示了如何使用CommonsPortletMultipartResolver

<bean id="portletMultipartResolver"
        class="org.springframework.web.portlet.multipart.CommonsPortletMultipartResolver">
    <!-- one of the properties available; the maximum file size in bytes -->
    <property name="maxUploadSize" value="100000"/>
</bean>

当然,您还需要在 classpath 中放置适当的 jars 以使 multipart 解析器工作。对于CommonsMultipartResolver,您需要使用commons-fileupload.jar。请务必至少使用 version 1.1 Commons _FileUpload,因为以前的版本不支持 JSR-286 Portlet applications。

现在您已经了解了如何将 Portlet MVC 设置为处理 multipart 请求,让我们来谈谈如何实际使用它。当DispatcherPortlet检测到 multipart 请求时,它会激活 context 中声明的解析器并移交请求。解析器然后做的是将ActionRequest包装在支持 multipart 文件上传的MultipartActionRequest中。使用MultipartActionRequest,您可以获得有关此请求包含的多部分的信息,并实际访问控制器中的 multipart files 本身。

请注意,您只能接收 multipart 文件上传作为ActionRequest的一部分,而不是作为RenderRequest的一部分。

25.7.2 处理表单中的文件上载

PortletMultipartResolver完成 job 后,请求将像其他任何一样处理。要使用PortletMultipartResolver,请创建一个带有上传字段的表单(请参阅下面的 example),然后让 Spring 将文件绑定到表单上(支持 object)。要让用户实际上传文件,我们必须创建一个(JSP/HTML)表单:

<h1>Please upload a file</h1>
<form method="post" action="<portlet:actionURL/>" enctype="multipart/form-data">
    <input type="file" name="file"/>
    <input type="submit"/>
</form>

如您所见,我们创建了一个名为“file”的字段,该字段与保存byte[] array 的 bean 的 property 相匹配。此外,我们添加了编码属性(enctype="multipart/form-data"),这是让浏览器知道如何编码 multipart 字段所必需的(不要忘记这一点!)。

就像任何其他不能自动转换为 string 或基本类型的 property 一样,为了能够将二进制数据放入 objects 中,您必须使用PortletRequestDataBinder注册自定义编辑器。有几个编辑器可用于处理 files 并在 object 上设置结果。有一个StringMultipartFileEditor能够将 files 转换为 Strings(使用 user-defined 字符集),并且有一个ByteArrayMultipartFileEditor将 files 转换为字节数组。它们的功能类似于CustomDateEditor

因此,为了能够使用表单上传 files,请声明解析器,映射到将处理 bean 的控制器以及控制器本身。

<bean id="portletMultipartResolver"
        class="org.springframework.web.portlet.multipart.CommonsPortletMultipartResolver"/>

<bean class="org.springframework.web.portlet.handler.PortletModeHandlerMapping">
    <property name="portletModeMap">
        <map>
            <entry key="view" value-ref="fileUploadController"/>
        </map>
    </property>
</bean>

<bean id="fileUploadController" class="examples.FileUploadController">
    <property name="commandClass" value="examples.FileUploadBean"/>
    <property name="formView" value="fileuploadform"/>
    <property name="successView" value="confirmation"/>
</bean>

之后,创建控制器和实际的 class 以保存文件 property。

public class FileUploadController extends SimpleFormController {

    public void onSubmitAction(ActionRequest request, ActionResponse response,
            Object command, BindException errors) throws Exception {

        // cast the bean
        FileUploadBean bean = (FileUploadBean) command;

        // let's see if there's content there
        byte[] file = bean.getFile();
        if (file == null) {
            // hmm, that's strange, the user did not upload anything
        }

        // do something with the file here
    }

    protected void initBinder(PortletRequest request,
            PortletRequestDataBinder binder) throws Exception {
        // to actually be able to convert Multipart instance to byte[]
        // we have to register a custom editor
        binder.registerCustomEditor(byte[].class, new ByteArrayMultipartFileEditor());
        // now Spring knows how to handle multipart object and convert
    }

}

public class FileUploadBean {

    private byte[] file;

    public void setFile(byte[] file) {
        this.file = file;
    }

    public byte[] getFile() {
        return file;
    }

}

如您所见,FileUploadBean具有byte[]类型的 property 来保存文件。控制器注册一个自定义编辑器,让 Spring 知道如何将解析器找到的 multipart object 实际转换为 bean 指定的 properties。在这个例子中,bean 本身的byte[] property 没有做任何事情,但实际上你可以做任何你想做的事情(将它保存在数据库中,邮寄给某人等)。

一个等效的 example,其中文件直接绑定到表单支持 object 上的 String-typed property 可能如下所示:

public class FileUploadController extends SimpleFormController {

    public void onSubmitAction(ActionRequest request, ActionResponse response,
            Object command, BindException errors) throws Exception {

        // cast the bean
        FileUploadBean bean = (FileUploadBean) command;

        // let's see if there's content there
        String file = bean.getFile();
        if (file == null) {
            // hmm, that's strange, the user did not upload anything
        }

        // do something with the file here
    }

    protected void initBinder(PortletRequest request,
            PortletRequestDataBinder binder) throws Exception {

        // to actually be able to convert Multipart instance to a String
        // we have to register a custom editor
        binder.registerCustomEditor(String.class, new StringMultipartFileEditor());
        // now Spring knows how to handle multipart objects and convert
    }
}

public class FileUploadBean {

    private String file;

    public void setFile(String file) {
        this.file = file;
    }

    public String getFile() {
        return file;
    }
}

当然,这个最后一个例子只能在上传纯文本文件的 context 中产生(逻辑)意义(在上传图像文件的情况下它不会那么好用)。

第三个(也是最后一个)选项是直接绑定到(表单支持)object 的 class 上声明的MultipartFile property。在这种情况下,不需要注册任何自定义 property 编辑器,因为没有要执行的类型转换。

public class FileUploadController extends SimpleFormController {

    public void onSubmitAction(ActionRequest request, ActionResponse response,
            Object command, BindException errors) throws Exception {

        // cast the bean
        FileUploadBean bean = (FileUploadBean) command;

        // let's see if there's content there
        MultipartFile file = bean.getFile();
        if (file == null) {
            // hmm, that's strange, the user did not upload anything
        }

        // do something with the file here
    }
}

public class FileUploadBean {

    private MultipartFile file;

    public void setFile(MultipartFile file) {
        this.file = file;
    }

    public MultipartFile getFile() {
        return file;
    }

}

25.8 处理 exceptions

就像 Servlet MVC 一样,Portlet MVC 提供了HandlerExceptionResolver,以减轻在请求与匹配请求的处理程序处理时发生的意外 exceptions 的痛苦。 Portlet MVC 还提供 portlet-specific,具体SimpleMappingExceptionResolver,使您可以获取可能被抛出的任何 exception 的 class name 并将其 map 映射到视图 name。

25.9 Annotation-based controller configuration

Spring 2.5 为 MVC 控制器引入了 annotation-based 编程 model,使用注释,如@RequestMapping@RequestParam@ModelAttribute等。这个 annotation 支持可用于 Servlet MVC 和 Portlet MVC。以此样式实现的控制器不必扩展特定的 base classes 或实现特定的接口。此外,它们通常不直接依赖于 Servlet 或 Portlet API,尽管如果需要,它们可以轻松访问 Servlet 或 Portlet 工具。

以下部分介绍了这些注释以及它们在 Portlet 环境中最常用的方式。

25.9.1 为 annotation 支持设置调度程序

仅当调度程序中存在相应的HandlerMapping(对于类型 level annotations)and/or HandlerAdapter(对于方法 level annotations)时,才会处理@RequestMapping。默认情况下,DispatcherServletDispatcherPortlet都是这种情况。

但是,如果要定义自定义HandlerMappingsHandlerAdapters,则需要确保定义相应的自定义DefaultAnnotationHandlerMapping and/or AnnotationMethodHandlerAdapter - 前提是您打算使用@RequestMapping

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="org.springframework.web.portlet.mvc.annotation.DefaultAnnotationHandlerMapping"/>

    <bean class="org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>

    // ... (controller bean definitions) ...

</beans>

如果要自定义映射策略,则明确定义DefaultAnnotationHandlerMapping and/or AnnotationMethodHandlerAdapter也是有意义的。指定自定义WebBindingInitializer(见下文)。

25.9.2 用 @Controller 定义控制器

@Controller annotation 表示特定的 class 用作控制器的角色。无需扩展任何控制器 base class 或 reference Portlet API。如果需要,您当然仍然可以 reference Portlet-specific features。

@Controller annotation 的基本目的是作为带注释的 class 的构造型,表明它的作用。调度程序将扫描这些带注释的 classes 以查找映射方法,检测@RequestMapping 注释(请参阅下一节)。

可以使用调度程序的 context 中的标准 Spring bean 定义显式定义带注释的控制器 beans。但是,@Controller构造型还允许自动检测,与 Spring 2.5 的一般支持一致,用于检测 classpath 中的 component classes 和 auto-registering bean 定义。

要启用此类带注释控制器的自动检测,您必须将 component 扫描添加到 configuration。这可以通过使用 spring-context schema 轻松实现,如以下 XML 片段所示:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="org.springframework.samples.petportal.portlet"/>

    // ...

</beans>

25.9.3 使用 @RequestMapping 映射请求

@RequestMapping annotation 用于将'VIEW'/'EDIT'等 portlet 模式映射到整个 class 或特定的处理程序方法。通常,type-level annotation maps 将特定模式(或模式加参数条件)映射到表单控制器上,并使用额外的 method-level annotations'缩小'特定 portlet 请求参数的主映射。

level 类型的@RequestMapping也可用于Controller接口的普通 implementation。在这种情况下,请求处理 code 将遵循传统的handle(Action|Render)Request签名,而控制器的映射将通过@RequestMapping annotation 表示。这适用于 pre-built Controller base classes,例如SimpleFormController

在下面的讨论中,我们将重点介绍基于带注释的处理程序方法的控制器。

以下是使用此 annotation 的 PetPortal sample application 中的表单控制器的示例:

@Controller
@RequestMapping("EDIT")
@SessionAttributes("site")
public class PetSitesEditController {

    private Properties petSites;

    public void setPetSites(Properties petSites) {
        this.petSites = petSites;
    }

    @ModelAttribute("petSites")
    public Properties getPetSites() {
        return this.petSites;
    }

    @RequestMapping // default (action=list)
    public String showPetSites() {
        return "petSitesEdit";
    }

    @RequestMapping(params = "action=add") // render phase
    public String showSiteForm(Model model) {
        // Used for the initial form as well as for redisplaying with errors.
        if (!model.containsAttribute("site")) {
            model.addAttribute("site", new PetSite());
        }

        return "petSitesAdd";
    }

    @RequestMapping(params = "action=add") // action phase
    public void populateSite(@ModelAttribute("site") PetSite petSite,
            BindingResult result, SessionStatus status, ActionResponse response) {
        new PetSiteValidator().validate(petSite, result);
        if (!result.hasErrors()) {
            this.petSites.put(petSite.getName(), petSite.getUrl());
            status.setComplete();
            response.setRenderParameter("action", "list");
        }
    }

    @RequestMapping(params = "action=delete")
    public void removeSite(@RequestParam("site") String site, ActionResponse response) {
        this.petSites.remove(site);
        response.setRenderParameter("action", "list");
    }
}

从 Spring 3.0 开始,有专用的@ActionMapping@RenderMapping(以及@ResourceMapping@EventMapping)注释可以代替使用:

@Controller
@RequestMapping("EDIT")
@SessionAttributes("site")
public class PetSitesEditController {

    private Properties petSites;

    public void setPetSites(Properties petSites) {
        this.petSites = petSites;
    }

    @ModelAttribute("petSites")
    public Properties getPetSites() {
        return this.petSites;
    }

    @RenderMapping // default (action=list)
    public String showPetSites() {
        return "petSitesEdit";
    }

    @RenderMapping(params = "action=add")
    public String showSiteForm(Model model) {
        // Used for the initial form as well as for redisplaying with errors.
        if (!model.containsAttribute("site")) {
            model.addAttribute("site", new PetSite());
        }

        return "petSitesAdd";
    }

    @ActionMapping(params = "action=add")
    public void populateSite(@ModelAttribute("site") PetSite petSite,
            BindingResult result, SessionStatus status, ActionResponse response) {
        new PetSiteValidator().validate(petSite, result);
        if (!result.hasErrors()) {
            this.petSites.put(petSite.getName(), petSite.getUrl());
            status.setComplete();
            response.setRenderParameter("action", "list");
        }
    }

    @ActionMapping(params = "action=delete")
    public void removeSite(@RequestParam("site") String site, ActionResponse response) {
        this.petSites.remove(site);
        response.setRenderParameter("action", "list");
    }
}

25.9.4 支持的处理程序方法 arguments

使用@RequestMapping注释的处理程序方法允许具有非常灵活的签名。它们可能具有以下类型的 arguments,任意 order(验证结果除外,如果需要,需要在相应的命令 object 之后执行):

  • 请求 and/or response objects(Portlet API)。您可以选择任何特定的 request/response 类型,e.g. PortletRequest/ActionRequest/RenderRequest。显式声明的 action/render 参数也用于将特定请求类型映射到处理程序方法(如果没有给出区分操作和呈现请求的其他信息)。

  • Session object(Portlet API):类型为 PortletSession。这种类型的参数将强制存在相应的 session。因此,这样的论证永远不会null

  • org.springframework.web.context.request.WebRequestorg.springframework.web.context.request.NativeWebRequest。允许通用请求参数访问以及 request/session 属性访问,而不与原生 Servlet/Portlet API 绑定。

  • java.util.Locale表示当前请求 locale(Portlet 环境中的门户 locale)。

  • java.util.TimeZone/java.time.ZoneId表示当前请求 time zone。

  • java.io.InputStream/java.io.Reader用于访问请求的内容。这将是 Portlet API 公开的原始 InputStream/Reader。

  • java.io.OutputStream/java.io.Writer用于生成响应的内容。这将是 Portlet API 公开的原始 OutputStream/Writer。

  • @RequestParam用于访问特定 Portlet 请求参数的注释参数。参数值将转换为声明的方法参数类型。

  • java.util.Map/org.springframework.ui.Model/org.springframework.ui.ModelMap用于丰富将暴露给 web 视图的隐式 model。

  • Command/form objects 将参数绑定到:as bean properties 或 fields,具有可自定义的类型转换,具体取决于@InitBinder方法 and/or HandlerAdapter configuration - 请参阅AnnotationMethodHandlerAdapter上的“webBindingInitializer”property。这些命令 objects 及其验证结果将作为 model 属性公开,默认情况下使用 property 表示法中的 non-qualified 命令 class name(e.g. “orderAddress”表示类型“mypackage.OrderAddress”)。指定 parameter-level ModelAttribute annotation 以声明特定的 model 属性 name。

  • 前一个 command/form object(前一个参数)的org.springframework.validation.Errors/org.springframework.validation.BindingResult验证结果。

  • org.springframework.web.bind.support.SessionStatus status 句柄,用于将表单处理标记为完成(触发清理_se属性,这些属性已由处理程序类型 level 处的@SessionAttributes annotation 指示)。

处理程序方法支持以下 return 类型:

  • 一个ModelAndView object,model 隐式丰富了命令 objects 和@ModelAttribute注释 reference 数据访问器方法的结果。

  • 一个Model object,视图 name 通过RequestToViewNameTranslator隐式确定,model 隐式地使用命令 objects 和@ModelAttribute注释 reference 数据访问器方法的结果。

  • 一个Map object 用于公开 model,其中 view name 通过RequestToViewNameTranslator隐式确定,而 model 隐式丰富了命令 objects 和@ModelAttribute annotated reference 数据访问器方法的结果。

  • 一个View object,model 通过命令 objects 和@ModelAttribute annotated reference 数据访问器方法隐式确定。处理程序方法还可以通过声明Model参数以编程方式丰富 model(参见上文)。

  • value 被解释为 view name,model 通过命令 objects 和@ModelAttribute annotated reference 数据访问器方法隐式确定。处理程序方法还可以通过声明Model参数以编程方式丰富 model(参见上文)。

  • void如果方法自己处理响应(e.g. 直接写入响应内容)。

  • 任何其他 return 类型将被视为要向视图公开的单个 model 属性,使用方法 level 上通过@ModelAttribute指定的属性 name(或者基于 return 类型的 class name 的默认属性 name)。 model 将使用命令 objects 和@ModelAttribute annotated reference 数据访问器方法的结果进行隐式丰富。

25.9.5 使用 @RequestParam 将方法参数的请求参数绑定到方法参数

@RequestParam annotation 用于将请求参数绑定到控制器中的方法参数。

PetPortal sample application 中的以下 code 片段显示了用法:

@Controller
@RequestMapping("EDIT")
@SessionAttributes("site")
public class PetSitesEditController {

    // ...

    public void removeSite(@RequestParam("site") String site, ActionResponse response) {
        this.petSites.remove(site);
        response.setRenderParameter("action", "list");
    }

    // ...

}

默认情况下,使用此 annotation 的参数是必需的,但您可以通过设置@RequestParam's required attribute to false (e.g., @RequestParam(name =“id”,required=false)`)来指定参数是可选的。

25.9.6 使用 @ModelAttribute 提供 model 数据的链接

@ModelAttribute在控制器中有两种使用场景。放置在方法参数上时,@ModelAttribute用于将 model 属性 map 映射到特定的带注释的方法参数(请参阅下面的populateSite()方法)。这就是控制器如何获取 object 对 object 持有表单中输入的数据。此外,可以将参数声明为支持 object 的表单的特定类型,而不是通用java.lang.Object,从而提高类型安全性。

方法 level 也使用@ModelAttribute来为 model 提供 reference 数据(参见下面的getPetSites()方法)。对于此用法,方法签名可以包含与上面针对@RequestMapping annotation 记录的相同类型。

@ModelAttribute带注释的方法将在选定的@RequestMapping带注释的处理程序方法之前执行。它们实际上是 pre-populate 具有特定属性的隐式 model,通常从数据库加载。然后,可以通过所选处理程序方法中的@ModelAttribute带注释的处理程序方法参数访问此类属性,可能应用了 binding 和验证。

以下 code 代码段显示了此 annotation 的这两个用法:

@Controller
@RequestMapping("EDIT")
@SessionAttributes("site")
public class PetSitesEditController {

    // ...

    @ModelAttribute("petSites")
    public Properties getPetSites() {
        return this.petSites;
    }

    @RequestMapping(params = "action=add") // action phase
    public void populateSite( @ModelAttribute("site") PetSite petSite, BindingResult result, SessionStatus status, ActionResponse response) {
        new PetSiteValidator().validate(petSite, result);
        if (!result.hasErrors()) {
            this.petSites.put(petSite.getName(), petSite.getUrl());
            status.setComplete();
            response.setRenderParameter("action", "list");
        }
    }
}

25.9.7 使用 @SessionAttributes 在 Session 中指定 store 的属性

type-level @SessionAttributes annotation 声明特定处理程序使用的 session 属性。这通常会列出 model 属性的名称或 model 属性的类型,这些属性应该透明地存储在 session 或某些会话存储中,在后续请求之间充当 form-backing beans。

以下 code 代码段显示了此 annotation 的用法:

@Controller
@RequestMapping("EDIT")
@SessionAttributes("site")
public class PetSitesEditController {
    // ...
}

25.9.8 自定义 WebDataBinder 初始化

要通过 Spring 的WebDataBinder使用 PropertyEditors 等自定义请求参数 binding,您可以在控制器中使用@InitBinder -annotated 方法,也可以通过提供自定义WebBindingInitializer来外部化 configuration。

使用 @InitBinder 自定义数据 binding

使用@InitBinder注释控制器方法允许您直接在控制器 class 中配置 web 数据 binding。 @InitBinder标识初始化WebDataBinder的方法,该方法将用于填充命令和形式 object arguments 的带注释的处理程序方法。

这样的 init-binder 方法支持@RequestMapping支持的所有 arguments,但 command/formobjects 和相应的验证结果 objects 除外。 Init-binder 方法不能有 return value。因此,它们通常被声明为void。典型的 arguments 包括WebDataBinderWebRequestjava.util.Locale的组合,允许 code 注册 context-specific 编辑器。

以下 example 演示了使用@InitBinder为所有java.util.Date form properties 配置CustomDateEditor

@Controller
public class MyFormController {

    @InitBinder
    protected void initBinder(WebDataBinder binder) {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        dateFormat.setLenient(false);
        binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
    }

    // ...

}

配置自定义 WebBindingInitializer

要外部化数据 binding 初始化,您可以提供WebBindingInitializer接口的自定义 implementation,然后通过为AnnotationMethodHandlerAdapter提供自定义 bean configuration 来启用它,从而覆盖默认的 configuration。

25.10 Portlet application 部署

部署 Spring Portlet MVC application 的 process 与部署任何 JSR-286 Portlet application 没有什么不同。然而,这个领域总体上令人困惑,因此值得简单谈谈。

通常,portal/portlet 容器在 servlet 容器中的一个 webapp 中运行,而 portlet run 在 servlet 容器中的另一个 webapp 中运行。在 portlet 容器 webapp 的 order 中,要使 calls 进入你的 portlet webapp,它必须使 cross-context calls 到 well-known _servall,它提供对portlet.xml文件中定义的 portlet 服务的访问。

JSR-286 规范没有具体说明这应该如何发生,因此每个 portlet 容器都有自己的机制,这通常涉及某种“deployment process”,它对 portlet webapp 本身进行更改,然后在 portlet 容器中注册 portlet。 。

至少,portlet webapp 中的web.xml文件被修改为 inject portlet 容器将调用的 well-known servlet。在某些情况下,单个 servlet 将为 webapp 中的所有 portlet 提供服务,在其他情况下,每个 portlet 都会有一个 servlet 实例。

一些 portlet 容器也会将 libraries and/or configuration files 注入到 webapp 中。 portlet 容器还必须使您的 webapp 可以使用 Portlet JSP Tag Library 的 implementation。

底部 line 是了解目标门户的部署需求并确保它们得到满足(通常遵循它提供的自动部署 process)非常重要。请务必仔细查看门户网站上有关此 process 的文档。

部署 portlet 后,请查看生成的web.xml文件以获得完整性。已知一些较旧的门户网站会破坏ViewRendererServlet的定义,从而破坏了 portlet 的呈现。

Updated at: 5 months ago
24.6. 更多资源Table of content26. WebSocket 支持