25. Portlet MVC框架

25.1 简介


JSR-286 The Java Portlet Specification

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


除了支持传统的(基于servlet的)Web开发之外,Spring还支持JSR-286 Portlet开发。 Portlet MVC框架尽可能地是Web MVC框架的镜像,并且还使用相同的底层视图抽象和集成技术。因此,在继续本章之前,请务必查看 Headers 为 Chapter 22, Web MVC frameworkChapter 23, View Technologies 的章节。

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

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


Spring Web Flow

Spring Web Flow(SWF)旨在成为Web应用程序页面流管理的最佳解决方案。

SWF在Servlet和Portlet环境中与Spring MVC和JSF等现有框架集成。如果您的业务流程(或流程)可以从会话模型中受益而不是纯粹的请求模型,那么SWF可能就是解决方案。

SWF允许您将逻辑页面流捕获为可在不同情况下重用的自包含模块,因此非常适合构建Web应用程序模块,以引导用户完成驱动业务流程的受控导航。

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


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

该框架围绕 DispatcherPortlet 设计,它使用可配置的处理程序映射和视图解析来向处理程序分派请求,就像Web框架中的 DispatcherServlet 一样。也以相同的方式支持文件上载。

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

25.1.1 控制器 - MVC中的C.

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

  • void handleActionRequest(request,response)

  • ModelAndView handleRenderRequest(request,response)

该框架还包括大多数相同的控制器实现层次结构,例如 AbstractControllerSimpleFormController 等。数据绑定,命令对象使用,模型处理和视图解析都与servlet框架中的相同。

25.1.2 Views - MVC中的V.

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

25.1.3 Web范围的bean

Spring Portlet MVC支持bean,其生命周期范围限定为当前HTTP请求或HTTP Session (正常和全局)。这不是Spring Portlet MVC本身的特定功能,而是Spring Portlet MVC使用的 WebApplicationContext 容器。这些bean作用域在 Section 7.5.4, “Request, session, global session, application, and WebSocket scopes” 中有详细描述

25.2 DispatcherPortlet

Portlet MVC是一个请求驱动的Web MVC框架,围绕一个portlet设计,该portlet将请求分派给控制器并提供便于开发portlet应用程序的其他功能。然而,Spring的 DispatcherPortlet 不仅如此。它与Spring ApplicationContext 完全集成,允许您使用Spring的所有其他功能。

与普通portlet一样, DispatcherPortlet 在Web应用程序的 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框架中,每个 DispatcherPortlet 都有自己的 WebApplicationContext ,它继承了Root WebApplicationContext 中已定义的所有bean。可以在特定于portlet的作用域中重写这些继承的bean,并且可以在给定的portlet实例的本地定义新的作用域特定的bean。

在初始化 DispatcherPortlet 时,框架将在Web应用程序的 WEB-INF 目录中查找名为 [portlet-name]-portlet.xml 的文件,并创建在那里定义的bean(覆盖在全局范围内使用相同名称定义的任何bean的定义)。

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

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

Table 25.1. Special beans in the WebApplicationContext

表达式说明
处理程序映射Section 25.5, “Handler mappings” )预处理器和后处理器及控制器的列表,如果它们符合某些条件,将执行(例如,与控制器指定的匹配portlet模式)
controller(s)Section 25.4, “Controllers” )bean提供实际功能(或至少访问功能)作为MVC三元组的一部分
视图解析器Section 25.6, “Views and resolving them” )能够解析视图名称以查看定义
multipart resolverSection 25.7, “Multipart (file upload) support” )提供处理来自HTML表单的文件上传的功能
处理程序异常解析程序Section 25.8, “Handling exceptions” )提供了将异常映射到视图或实现其他更复杂的异常处理代码的功能

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

  • PortletRequest.getLocale() 返回的语言环境绑定到请求,以允许进程中的元素解析处理请求时使用的语言环境(呈现视图,准备数据等)。

  • 如果指定了多部分解析程序并且这是 ActionRequest ,则检查请求的多部分,如果找到它们,则将其包装在 MultipartActionRequest 中,以供进程中的其他元素进一步处理。 (有关多部分处理的更多信息,请参见 Section 25.7, “Multipart (file upload) support” )。

  • 搜索适当的处理程序。如果找到处理程序,则将执行与处理程序(预处理程序,后处理程序,控制器)关联的执行链以准备模型。

  • 如果返回模型,则使用已使用 WebApplicationContext 配置的视图解析器呈现视图。如果没有返回模型(这可能是由于处理器之前或之后拦截请求,例如,因为安全原因),没有呈现视图,因为请求已经可以实现。

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

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

Table 25.2. DispatcherPortlet initialization parameters

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

25.3 ViewRendererServlet

Portlet MVC中的呈现过程比Web MVC中的更复杂。为了重用Spring Web MVC中的所有 view technologies ,我们必须将 PortletRequest / PortletResponse 转换为 HttpServletRequest / HttpServletResponse ,然后调用 Viewrender 方法。为此, DispatcherPortlet 使用为此目的而存在的特殊servlet: ViewRendererServlet

为了使 DispatcherPortlet 渲染起作用,您必须在Web应用程序的 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 键下的属性。

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

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

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

可以使用 DispatcherPortlet’sviewRendererUrl配置参数更改ViewRendererServlet` 的实际URL。

25.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 接口仅定义每个控制器所需的最常用功能:处理操作请求,处理呈现请求以及返回模型和视图。

25.4.1 AbstractController和PortletContentGenerator

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

Table 25.3. Features offered by the AbstractController

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

requireSessioncacheSeconds 属性在 PortletContentGenerator 类上声明,该类是 AbstractController 的超类,但为了完整性,此处包括在内。

当使用 AbstractController 作为控制器的基类时(不建议这样做,因为有许多其他控制器可能已经为您完成了这项工作),您只需要覆盖 handleActionRequestInternal(ActionRequest, ActionResponse) 方法或 handleRenderRequestInternal(RenderRequest, RenderResponse) 方法(或两者) ,实现你的逻辑,并返回一个 ModelAndView 对象(在 handleRenderRequestInternal 的情况下)。

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

以下是由Web应用程序上下文中的类和声明组成的简短示例。

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>

除了设置处理程序映射(参见 Section 25.5, “Handler mappings” )以使这个非常简单的控制器工作之外,上面的类和Web应用程序上下文中的声明是您所需要的。

25.4.2 其他简单的控制器

虽然您可以扩展 AbstractController ,但Spring Portlet MVC提供了许多具体实现,这些实现提供了在简单MVC应用程序中常用的功能。

ParameterizableViewController 与上面的示例基本相同,只是您可以指定它将在Web应用程序上下文中返回的视图名称(不需要对视图名称进行硬编码)。

PortletModeNameViewController 使用portlet的当前模式作为视图名称。因此,如果您的portlet处于查看模式(即 PortletMode.VIEW ),那么它将使用 "view" 作为视图名称。

25.4.3 命令控制器

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

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

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

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

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

这些命令控制器非常强大,但它们确实需要详细了解它们的运行方式才能有效地使用它们。仔细查看整个层次结构的javadoc,然后在开始使用它们之前查看一些示例实现。

25.4.4 PortletWrappingController

可以使用现有的portlet并将请求映射到 DispatcherPortlet ,而不是开发新的控制器。使用 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>

这可能非常有 Value ,因为您可以使用拦截器预处理和后处理发送到这些portlet的请求。由于JSR-286不支持任何类型的过滤机制,因此非常方便。例如,这可以用于围绕MyFaces JSF Portlet包装Hibernate OpenSessionInViewInterceptor

25.5 处理程序映射

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

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

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

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

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

本节的其余部分描述了Spring Portlet MVC中最常用的三个处理程序映射。它们都扩展 AbstractHandlerMapping 并共享以下属性:

  • interceptors :要使用的拦截器列表。 HandlerInterceptorSection 25.5.4, “Adding HandlerInterceptors” 中讨论过。

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

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

  • lazyInitHandlers :允许对单例处理程序进行延迟初始化(原型处理程序总是被懒惰地初始化)。默认值为false。此属性直接在三个具体的处理程序中实现。

25.5.1 PortletModeHandlerMapping

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

<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模式的情况下导航到多个控制器,最简单的方法是使用一个request参数作为控制映射的键。

ParameterHandlerMapping 使用特定请求参数的值来控制映射。参数的默认名称是 'action' ,但可以使用 'parameterName' 属性进行更改。

此映射的bean配置如下所示:

<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

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

同样,参数的默认名称是 "action" ,但可以使用 parameterName 属性进行更改。

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

此映射的bean配置如下所示:

<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版本一样,这个接口定义了三个方法:一个将在执行实际处理程序之前调用( preHandle ),一个将在执行处理程序之后调用( postHandle ),另一个在完成之后调用。请求已完成( afterCompletion )。这三种方法应该提供足够的灵活性来进行各种预处理和后处理。

preHandle 方法返回一个布尔值。您可以使用此方法来中断或继续执行链的处理。当此方法返回 true 时,处理程序执行链将继续。当它返回 false 时, DispatcherPortlet 假定拦截器本身已处理请求(例如,呈现了适当的视图),并且不继续执行执行链中的其他拦截器和实际处理程序。

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

25.5.5 HandlerInterceptorAdapter

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

25.5.6 ParameterMappingInterceptor

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

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

25.6 查看并解决它们

如前所述,Spring Portlet MVC直接重用Spring Web MVC中的所有视图技术。这不仅包括各种 View 实现本身,还包括 ViewResolver 实现。有关更多信息,请分别参阅 Chapter 23, View TechnologiesSection 22.5, “Resolving views”

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

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

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

  • 可以在Portlet MVC中使用 'forward:' 前缀。但是,请记住,因为您在portlet中,所以您不知道当前的URL是什么样的。这意味着您不能使用相对URL访问Web应用程序中的其他资源,并且您必须使用绝对URL。

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

25.7 Multipart(文件上传)支持

Spring Portlet MVC具有内置的多部分支持,可以处理portlet应用程序中的文件上传,就像Web MVC一样。多部分支持的设计是使用 org.springframework.web.portlet.multipart 包中定义的可插入 PortletMultipartResolver 对象完成的。 Spring提供 PortletMultipartResolverCommons FileUpload 一起使用。本节其余部分将介绍如何支持上载文件。

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

任何已配置的PortletMultipartResolver bean必须具有以下id(或名称):“portletMultipartResolver”。如果已使用任何其他名称定义了`PortletMultipartResolver,则DispatcherPortlet将找不到您的PortletMultipartResolver,因此不会有多部分支持生效。

25.7.1 使用PortletMultipartResolver

以下示例显示如何使用 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>

当然,您还需要在类路径中放置适当的jar,以使多部分解析器工作。对于 CommonsMultipartResolver ,您需要使用 commons-fileupload.jar 。务必至少使用Commons FileUpload 1.1版,因为以前的版本不支持JSR-286 Portlet应用程序。

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

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

25.7.2 处理表单中的文件上载

PortletMultipartResolver 完成其工作后,请求将像任何其他处理一样处理。要使用 PortletMultipartResolver ,请创建带有上载字段的表单(请参阅下面的示例),然后让Spring将文件绑定到表单(后备对象)上。要让用户实际上传文件,我们必须创建一个(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[] 数组的bean的属性相匹配。此外,我们添加了编码属性( enctype="multipart/form-data" ),这是让浏览器知道如何编码多部分字段所必需的(不要忘记这一点!)。

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

因此,为了能够使用表单上传文件,请声明解析器,映射到将处理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>

之后,创建控制器和实际类来保存文件属性。

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[] 类型的属性。控制器注册一个自定义编辑器,让Spring知道如何将解析器找到的多部分对象实际转换为bean指定的属性。在这个例子中,没有对bean本身的 byte[] 属性做任何事情,但实际上你可以做任何你想做的事情(将它保存在数据库中,邮寄给某人等)。

将文件直接绑定到表单支持对象上的String类型属性的等效示例可能如下所示:

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;
    }
}

当然,这个最后一个例子仅在上传纯文本文件的上下文中具有(逻辑)意义(在上载图像文件的情况下它不会很好地工作)。

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

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 处理异常

就像Servlet MVC一样,Portlet MVC提供了 HandlerExceptionResolver 来减轻在请求与匹配请求的处理程序处理请求时发生的意外异常的痛苦。 Portlet MVC还提供了一个特定于portlet的具体 SimpleMappingExceptionResolver ,使您可以获取可能抛出的任何异常的类名并将其映射到视图名称。

25.9 基于注释的控制器配置

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

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

25.9.1 设置注释支持的调度程序

只有在调度程序中存在相应的 HandlerMapping (用于类型级别注释)和/或 HandlerAdapter (用于方法级别注释)时,才会处理“@ RequestMapping”。默认情况下, DispatcherServletDispatcherPortlet 都是这种情况。

但是,如果要定义自定义 HandlerMappingsHandlerAdapters ,则需要确保定义相应的自定义 DefaultAnnotationHandlerMapping 和/或 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 和/或 AnnotationMethodHandlerAdapter 也是有意义的。指定自定义 WebBindingInitializer (见下文)。

25.9.2 使用@Controller定义控制器

@Controller 注释表示特定类用作控制器的角色。无需扩展任何控制器基类或引用Portlet API。如果需要,您当然仍然可以参考特定于Portlet的功能。

@Controller 注释的基本目的是充当注释类的构造型,指示其作用。调度程序将扫描这些带注释的类以查找映射方法,检测 @RequestMapping 注释(请参阅下一节)。

可以使用调度程序上下文中的标准Spring bean定义显式定义带注释的控制器bean。但是, @Controller 构造型还允许自动检测,与Spring 2.5对类路径中检测组件类的一般支持以及为它们自动注册bean定义保持一致。

要启用此类带注释控制器的自动检测,您必须将组件扫描添加到配置中。通过使用以下XML片段中所示的spring-context架构可以轻松实现这一点:

<?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 注释用于将“VIEW”/“EDIT”等portlet模式映射到整个类或特定的处理程序方法。通常,类型级注释将特定模式(或模式加参数条件)映射到表单控制器上,其他方法级注释“缩小”特定portlet请求参数的主映射。

类型级别的@RequestMapping也可用于Controller接口的简单实现。在这种情况下,请求处理代码将遵循传统句柄(Action | Render)请求签名,而控制器的映射将通过@RequestMapping注释表示。这适用于预构建的Controller基类,例如SimpleFormController。在下面的讨论中,我们将重点介绍基于带注释的处理程序方法的控制器。

以下是使用此批注的PetPortal示例应用程序中的表单控制器的示例:

@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 支持的处理程序方法参数

使用 @RequestMapping 注释的处理程序方法允许具有非常灵活的签名。它们可能具有以下类型的参数,以任意顺序排列(验证结果除外,如果需要,需要紧跟在相应的命令对象之后):

  • 请求和/或响应对象(Portlet API)。您可以选择任何特定的请求/响应类型,例如PortletRequest / ActionRequest / RenderRequest。显式声明的action / render参数还用于将特定请求类型映射到处理程序方法(如果没有给出区分action和render请求的其他信息)。

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

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

  • java.util.Locale 用于当前请求区域设置(Portlet环境中的门户区域设置)。

  • java.util.TimeZone / java.time.ZoneId 为当前请求时区。

  • 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视图的隐式模型。

  • 用于将参数绑定到的命令/表单对象:作为bean属性或字段,具有可自定义的类型转换,具体取决于 @InitBinder 方法和/或HandlerAdapter配置 - 请参阅“ webBindingInitializer" property on AnnotationMethodHandlerAdapter 。此类命令对象及其验证结果将作为模型公开缺省情况下,使用属性表示法中的非限定命令类名称(例如 "orderAddress" 表示类型 "mypackage.OrderAddress" )。指定参数级 ModelAttribute 注释以声明特定的模型属性名称。

  • org.springframework.validation.Errors / org.springframework.validation.BindingResult 前面的命令/表单对象的验证结果(前一个参数)。

  • org.springframework.web.bind.support.SessionStatus 状态句柄,用于将表单处理标记为完成(触发清理在处理程序类型级别由 @SessionAttributes 注释指示的会话属性)。

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

  • A. ModelAndView 对象,模型隐式地丰富了命令对象和 @ModelAttribute 带注释的引用数据访问器方法的结果。

  • 一个 Model 对象,其视图名称通过 RequestToViewNameTranslator 隐式确定,并且模型隐式地使用命令对象和 @ModelAttribute 带注释的引用数据访问器方法的结果。

  • 用于公开模型的 Map 对象,其中视图名称通过 RequestToViewNameTranslator 隐式确定,并且模型隐式地使用命令对象和 @ModelAttribute 带注释的引用数据访问器方法的结果。

  • 一个 View 对象,该模型通过命令对象和 @ModelAttribute 带注释的引用数据访问器方法隐式确定。处理程序方法还可以通过声明 Model 参数(参见上文)以编程方式丰富模型。

  • 一个 String 值,它被解释为视图名称,模型通过命令对象和 @ModelAttribute 带注释的参考数据访问器方法隐式确定。处理程序方法还可以通过声明 Model 参数(参见上文)以编程方式丰富模型。

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

  • 任何其他返回类型将被视为要向视图公开的单个模型属性,使用在方法级别通过 @ModelAttribute 指定的属性名称(或者基于返回类型的类名称的默认属性名称)。该模型将使用命令对象和 @ModelAttribute 带注释的参考数据访问器方法的结果进行隐式丰富。

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

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

PetPortal示例应用程序中的以下代码片段显示了用法:

@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");
    }

    // ...

}

默认情况下,使用此批注的参数是必需的,但您可以通过将 @RequestParam’srequired属性设置为false(例如,@RequestParam(name="id", required=false)` )来指定参数是可选的。

25.9.6 使用@ModelAttribute提供模型中数据的链接

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

@ModelAttribute 也用于方法级别以提供模型的参考数据(请参阅下面的 getPetSites() 方法)。对于此用法,方法签名可以包含与上面针对 @RequestMapping 注释所记录的相同类型。

@ModelAttribute带注释的方法将在所选的@RequestMapping带注释的处理程序方法之前执行。它们有效地使用通常从数据库加载的特定属性预填充隐式模型。然后,可以通过所选处理程序方法中的@ModelAttribute注释处理程序方法参数访问此类属性,可能已对其应用绑定和验证。

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

@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指定要存储在会话中的属性

类型级 @SessionAttributes 注释声明特定处理程序使用的会话属性。这通常会列出模型属性的名称或模型属性的类型,这些属性应该透明地存储在会话或某些会话存储中,作为后续请求之间的表单支持bean。

以下代码段显示了此批注的用法:

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

25.9.8 自定义WebDataBinder初始化

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

使用@InitBinder自定义数据绑定

使用 @InitBinder 注释控制器方法允许您直接在控制器类中配置Web数据绑定。 @InitBinder 标识初始化 WebDataBinder 的方法,该方法将用于填充带注释的处理程序方法的命令和表单对象参数。

这样的init-binder方法支持 @RequestMapping 支持的所有参数,但命令/表单对象和相应的验证结果对象除外。 Init-binder方法不能有返回值。因此,它们通常被声明为 void 。典型的参数包括 WebDataBinderWebRequestjava.util.Locale 的组合,允许代码注册特定于上下文的编辑器。

以下示例演示如何使用 @InitBinder 为所有 java.util.Date 表单配置 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

要外部化数据绑定初始化,您可以提供 WebBindingInitializer 接口的自定义实现,然后通过为 AnnotationMethodHandlerAdapter 提供自定义Bean配置来启用该实现,从而覆盖默认配置。

25.10 Portlet应用程序部署

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

通常,portal / portlet容器在servlet容器中的一个webapp中运行,而portlet在servlet容器中的另一个webapp中运行。为了使portlet容器webapp能够调用你的portlet webapp,它必须对一个众所周知的servlet进行跨上下文调用,该servlet提供对 portlet.xml 文件中定义的portlet服务的访问。

JSR-286规范没有详细说明应该如何发生这种情况,因此每个portlet容器都有自己的机制,这通常涉及某种“部署过程”,它会对portlet webapp本身进行更改,然后在其中注册portlet。 portlet容器。

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

一些portlet容器也会将库和/或配置文件注入到webapp中。 portlet容器还必须使您的webapp可以使用Portlet JSP标记库的实现。

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

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

Updated at: 5 months ago
24.6. 更多资源Table of content26. WebSocket支持
Comment
You are not logged in.

There are no comments.