引言
Struts 是一个很受欢迎的开放源代码平台基础,它是为了使复杂的 Web 应用程序的创建和维护更加容易而设计的。Struts 框架支持模型-视图-控制器(Model-View-Controller,MVC)模型,并且提出了许多重要的应用程序设计和实现需要注意的事项。为了能够在门户应用程序中实现页面导航,WebSphere? Portal V5 提供了一些标记,在开发典型 portlet 和 Struts portlet 时可将这些标记用于 JavaServer Pages(JSP)。本文重点讨论 Struts 框架创建多模块的能力,并通过一个样本 Struts portlet 应用程序为您介绍如何采用 JSP 实现页面导航方法。
我们将关注以下两种互补的方法:
- 如何使用在 WebSphere Portal 中可用的某一 JSP 标记来从 JSP 实现页面导航
我们将会看到一个样本应用程序,包括开发环境和门户资源的屏幕快照,以及页面导航方法的代码样本。
- 如何使用 Struts portlet 框架中内置的多模块支持来组织 Web 导航方案
我们将会看到一些代码样本,它们阐述了如何使用页面导航特征来执行在门户环境中转换的多个 Struts 模块。
本文假定您已基本了解 Struts 应用程序,包括如何在 WebSphere Studio Application Developer 中创建 Struts 应用程序、如何将 Struts 作为 portlet 进行部署,同时也了解 WebSphere Portal 中的门户资源。(要获得这些方面的更多信息,请参见参考资料。)
在开发和部署我们的样本应用程序时用到了以下产品:
- WebSphere Portal V5.x
- WebSphere Application Server v5.x
- 支持 Struts Version 1.1 的 WebSphere Studio Application Developer v5.0.1 (下面称为 Application Developer)。
本文所讲解的用于实现页面导航的开发任务可同时用于标准 portlet 和 Struts portlet。
应用程序概述
在样本应用程序中,我们将会使用位于三个不同页面的三个 portlet。当用户选择其中某一 portlet 中的链接时,该用户会被定向到另一个页面,其中驻留着另一个 portlet。在整个文章中我们开发和提到的示例都会利用多 Struts 模块将 WebSphere Portal 页面导航标记并入门户环境中每个模块本身的 JSP 文件中。图 1 概括性地展示了样本应用程序将如何工作;为了把重点放在理解这些方法上,我们特意简化了样本应用程序。在本文的后面我们将会提供应用程序的细节。
图 1. 样本应用程序的高级视图

构建导航结构
WebSphere Portal 提供了自己的 JSP 标记来供 portlet JSP 使用。其中的一个标记,<wps:URLGeneration attribute="value"/>
,是用于创建到门户任何地方的页面链接的。我们使用该标记中一个属性,contentNode="id|name"
,来创建到不同页面的 URL,以便构建导航结构。该属性利用一个惟一的标识符(内容节点的名称)来指定可以从中找到该 portlet 的页面。(关于这一标记的更多信息请参见参考资料。)
在 WebSphere Portal 中选择 Administration => Portal Settings => Custom Unique Names。我们使用 Custom Unique Names 管理 portlet 来获取为 contentNode
属性指定的门户资源的惟一标识符。管理员可以使用 Custom Unique Names portlet 来查看惟一标识符并为门户资源分配(让人易读的)自定义名称。图 2 显示了这些已配置的样本页面的惟一标识符和自定义名称:
Struts_Test
Struts_Receiver
Struts_Receiver_2
图 2. Custom Unique Names portlet

在这个示例中,WebSphere Portal 分配给这些页面的惟一标识符分别为 6_0_LT
、6_0_P1
和 6_0_IP
。(您的值可能会有所不同。)在驻留 WebSphere Portal 的另一台机器上也创建了同样的门户资源 Struts_Test
、Struts_Receiver
和 Struts_Receiver_2
portlet 页面。为了阅读和工作的方便,我们也为这些样本页面配置自定义名称,并在 JSP 文件中使用这些自定义名称(而不是惟一标记符)。
在 <wps:URLGeneration attribute="value"/>
portlet JSP 标记主体中,可以使用 <% wpsURL %>
脚本变量将 URL 直接写入输出流。在这个示例中,我们使用以下 JSP 代码来创建从某一 JSP 页面到 Struts Receiver 页面的链接:
<wps:urlGeneration contentNode="Struts_Receiver_Page">
<A HREF="<%wpsURL.write(out);%>"style="text-decoration:none" >
Any text</a>
</wps:urlGeneration>
|
<wps:URLGeneration attribute="value"/>
portlet JSP 标记属于 engine.tld
标记库,这一标记库是由门户服务器引擎使用的。为了让该门户 JSP 标记可用于我们的 JSP 中,我们在 JSP 页面的最开始位置添加标记库指示 taglib
。如以下代码所示,通过前缀 wps
,我们样本中所有的 engine.tld
标记现在就都可用了:
<%@ taglib uri="/WEB-INF/tld/engine.tld" prefix="wps" %>
|
样本页面和 portlet 配置
该样本应用程序的页面导航条包括三个 portlet(这里提供的长名称有助于强调页面导航流程):
请记住,到目前为止所描述的全部任务均可用于常规 portlet 或 Struts portlet 开发。接下来我们将使用多 Struts 模块中的门户 JSP 标记来实现页面导航。
在 Struts 中使用多模块支持
Struts Version 1.1 可以让我们将单个 Struts 应用程序拆分为多个模块,每个模块都有自己的操作、JSP 页面和 Struts 配置文件(图 6)。在 Struts 应用程序中实现多 Struts 模块需要以下几个步骤:
- 创建独立的 Struts 配置文件以及每个应用程序模块的 Struts 操作和 JSPs。
- 配置 Web 部署描述符(
web.xml
)。
- 从一个模块转换到另一个模块。
虽然 Struts 应用程序很容易应用,但要想在门户环境中运行 Struts 应用程序的话,您还需要执行其他的步骤。(参见参考资料)。
图 6. Struts 应用程序中的多模块支持

创建独立的 Struts 模块和文件
对于我们的样本应用程序,我们创建了四个 Struts 模块,以及它们自己的 Struts 配置文件、行为和 JSPs。这些 Struts 模块是:
address
phone-numbers
overview
default
.
我们也配置了 web.xml
和 portlet.xml
文件,并使用 default
模块执行从一个模块到另一个模块的转换。
接下来,我们将会根据实现的需要讲解我们的文件需要的改动。
在 Struts 模块中开发页面导航
我们在以下三个模块中实现页面导航:
overview
address
phone-numbers
.
除了存在一些细微的差别以外,这些模块中每一个的页面导航开发任务几乎都是相同的。因此,我们将讲解实现 overview
模块的页面导航的任务,然后再描述一下其他模块的实现任务与此的差别。此外,我们还会讲解特定于 Struts 模块的开发过程,该模块是为 default
模块而转换的。在我们的实现中,default
模块用于将控制权交给下一个适当的模块;并不需要页面导航。然而,由于它启动其他模块来执行模块转换并且是导航结构的一个主要部分,所以我们这里也将它包括在内。
在 Application Developer 中,我们创建一个支持 Struts Version 1.1 的 Web 项目,然后为开发任务创建文件,如下面的表 1 所示。
表 1. 开发构件列表

以下是表 1 所示构件的简要描述:
- JSP 文件:
我们开发了五个 JSP 文件:
- 用于
default
模块的 index.jsp
和 failure.jsp
文件;
- 用于
address
模块的 address.jsp
文件;
- 用于
phone-numbers
模块的 phone-numbers.jsp
文件;
- 用于
overview
模块的 overview.jsp
文件。
- 操作:
我们在 com.test.strutstest.actions
包中创建四个 Struts 操作。它们的类为:
- DisplayAction
- DisplayAddressInfoAction
- DisplayPhoneInfoAction
- OverviewAction.
- 表单 bean:
我们在 com.test.strutstest.forms
包中创建两个表单 bean。它们的类为:
UserFormBean
OverviewFormBean
.
- 后端 bean 和模型类:
我们在包中使用一个后端 bean 类(UserBean )和一个模型类(UserBeanHelper)。(后端 bean 包含存根数据,并不与真正后端进行通信。)
- Struts 配置文件:
我们开发了四个 struts-config.xml
文件,每个模块使用一个:
- 用于
default
模块的 WEB-INF/struts-config.xml
- 用于
address
模块的 WEB-INF/address/struts-config.xml
- 用于
phone-numbers
模块的 WEB-INF/phone-numbers/struts-config.xml
- 用于
overview
模块的 WEB-INF/overview/struts-config.xml
。
- 其他配置文件:
在将 Struts 应用程序作为 portlet 进行部署时需要修改 web.xml
和 portlet.xml
文件。
以上的每一个配置文件都包含下列请求处理器(Request Processor):
<controller
processorClass="com.ibm.wps.portlets.struts.WpsRequestProcessor" >
|
图 7 显示了在 Application Developer 开发环境中开发构件的位置:
图 7. WebSphere Studio Application Developer 中的开发构件的位置

配置 default 模块
要想启动 default
模块并查看它如何在 Struts 模块之间进行转换,需要执行以下步骤:
- Default 模块是在 WebSphere Portal 调用
index.jsp
文件时启动的,这是在 web.xml
文件中的 <welcome-file-list>
标记中指定的:
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
|
- 调用
index.jsp
时,在 logic
Struts 标记中指定逻辑转发名 Display
:
<logic:forward name="Display"/>
|
- 将
default
模块的 struts-config.xml
文件中的逻辑 forward name
(在此为 Display
)定义为全局转发:
<global-forwards>
<forward name="Display" path="/display.do"/>
</global-forwards>
|
- 在
<global-forwards>
部分中,使逻辑全局转发上下文关系路径 path
="/ display.do"
与 <action-mappings>
部分中的操作元素的上下文关系路径 path="/display"
路径相匹配。这一匹配可以启动 DisplayAction 操作。操作映射部分(包括带有 path
和 type
属性的 <action>
元素)如下所示:
<action-mappings>
<action path="/display"
type="com.test.strutstest.actions.DisplayAction">
.................................................
</action>
</action-mappings>
|
为了启用模块转换,请为 remaining 模块配置 default
模块的 struts-config.xml
、portlet.xml
和 config.xml
文件:
- 配置
default
模块的 struts-config.xml
文件。我们使用 DisplayAction 作为我们的操作类和四个逻辑转发:
<action-mappings>
<action path="/display"
type="com.test.strutstest.actions.DisplayAction">
<forward name="failure"
contextRelative="true"
path="/failure.jsp"/>
<forward name="address"
contextRelative="true"
path="/address/start.do"/>
<forward name="phone-numbers"
contextRelative="true"
path="/phone-numbers/start.do"/>
<forward name="overview"
contextRelative="true"
path="/overview/start.do"/>
</action>
</action-mappings>
|
- 配置
portlet.xml
文件,使之符合 default
模块的 struts-config.xml
文件中的配置。特别是,在 default
模块的 struts-config.xml
文件中的四个逻辑转发名称中的每一个均要与在 portlet.xml
文件中设置的 view.forward
配置参数值相匹配。表 2 显示了这一映射关系:
表 2. view.forward 配置值对转发名称配置值

- 配置
web.xml
文件来定义不同的模块。我们使用 config
初始化参数来将我们的 default
模块告知操作 servlet;然而,对于其他的每个模块,我们列出了一个名为 config/module
的初始化参数,其中 module
是我们的模块名称。例如:
- 可以这样定义
default
模块:
<init-param>
<param-name>config</param-name>
<param-value>WEB-INF/struts-config.xml</param-value>
</init-param>
|
- 可以这样定义其他模块:
<init-param>
<param-name>config/address</param-name>
<param-value>WEB-INF/address/struts-config.xml
</param-value>
</init-param>
<init-param>
<param-name>config/phone-numbers</param-name>
<param-value>WEB-INF/phone-numbers/struts-config.xml
</param-value>
</init-param>
<init-param>
<param-name>config/overview</param-name>
<param-value>WEB-INF/overview/struts-config.xml
</param-value>
</init-param>
|
(其他模块配置和必要的 web.xml
配置设置请参见参考资料。)
- 对
web.xml
和 portlet.xml
文件执行标准更新来将 Struts 应用程序作为 portlet 进行部署。(要获得更多信息请参见参考资料和下载部分的样本 web.xml
和 portlet.xml
文件。)
正如 default
模块的 struts-config.xml
文件中的 <action-mapping>
部分所配置的,模块转换是在 DisplayAction 类中进行的。要想确定 WebSphere Portal 调用的是哪个模块,可以执行以下的步骤:
- 调用 DisplayAction 类的 execute() 方法时,访问的是在
portlet.xml
文件中设置的 view.forward
参数:
PortletSettings portletSettings = request.getPortletSettings();
String viewForward = portletSettings.getAttribute("view.forward");
|
- 对于我们的 portlet 来说,
view.forward
参数会有以下的值:
表 3. 样本 portlet 的 view.forward 配置参数的值

- 如果
viewForward
为空,那么在 execute() 方法中会将这一变量设置为 failure
。
- 已配置的
viewForward
值的转发映射是从 struts-config.xml
文件检索的:
ActionForward forward = new ActionForward();
try
{
forward = mapping.findForward(forwardName);
}
catch (Exception e)
{
...........................................
}
|
- execute() 方法中的最后步骤返回 ActionForward(
forward
)的实例:
配置其他 struts-config.xml
文件。根据 DisplayAction 类中的模块确定结果,如果 view.forward
参数值为:
address:
default
模块的 struts-config.xml
文件的 <action-mappings>
部分中匹配的 start
路径会使 DisplayAddressInfoAction 在 address
模块中调用。根据 web.xml
中的配置,这一过程需要使用到 address/struts-config.xml
文件。address/struts-config.xml
文件包含以下的设置:
<!-- ===== Global Forward Definitions ===== -->
<global-forwards>
<forward name="start" path="/start.do"/>
</global-forwards>
<!-- ===== Action Mapping Definitions ===== -->
<action-mappings>
<action path="/start"
type="com.test.strutstest.actions.DisplayAddressInfoAction"
name="userFormBean"
scope="request">
<forward name="success" path="/address.jsp"/>
</action
</action-mappings>
|
phone-numbers:
default
模块的 struts-config.xml
文件的 <action-mappings>
部分中匹配的 start
路径会使 DisplayPhoneInfoAction 在 phone-numbers
模块中调用。根据 web.xml
文件中的配置,这一过程需要使用到 phone-numbers/struts-config.xml
文件。phone-numbers/struts-config.xml
文件包含以下的设置:
<!-- ===== Global Forward Definitions ===== -->
<global-forwards>
<forward name="start" path="/start.do"/>
</global-forwards>
<!-- ===== Action Mapping Definitions ===== -->
<action-mappings>
<action path="/start"
type="com.test.strutstest.actions.DisplayPhoneInfoAction"
name="userFormBean"
scope="request">
<forward name="success" path="/phone-numbers.jsp"/>
</action
</action-mappings>
|
overview:
default
模块的 struts-config.xml
文件的 <action-mappings>
部分中匹配的 start
路径会使 OverviewAction 在 overview
模块中调用。根据 web.xml
文件中的配置,这一过程需要使用到 overview/struts-config.xml
文件。overview/struts-config.xml
包含以下的设置:
<!-- ===== Global Forward Definitions ===== -->
<global-forwards>
<forward name="start" path="/start.do"/>
</global-forwards>
<!-- ===== Action Mapping Definitions ===== -->
<action-mappings>
<action path="/start"
type="com.test.strutstest.actions.OverviewAction"
name="overviewFormBean"
scope="request">
<forward name="success" path="/Overview.jsp"/>
</action>
</action-mappings>
|
failure:
根据 default
模块的 struts-config.xml
文件中的配置,这种参数值下会启动 failure.jsp
。
到目前为止,我们已经创建了表 1 中所列出的文件,并根据我们的示例需要,对其中的大部分做了修改。现在我们就可以开始编写代码来实现使用 overview
模块的页面导航。以下部分将通过具体步骤指导您完成这一过程。
开发 overview 模块
要想生成 Overview.jsp
文件、OverviewAction 类、OverviewFormBean 类和 overview/struts-config.xml
文件来开发 Overview
Struts portlet,请在操作类和 JSP 文件中遵循这些开发任务:
- 一旦控制权从
default
模块转移到 Overview
模块,OverviewAction 类中的 execute() 方法会执行以下这些任务:
- 访问
OverviewFormBean
表单 bean 并设置以下属性:
try
{
String forwardName = null;
if (form == null)
{
form = (ActionForm) request.getAttribute("overviewFormBean");
}
//Set-up form bean fields
OverviewFormBean formBean = (OverviewFormBean) form;
formBean.setPhoneNumbersLinkName("List Phone Numbers");
formBean.setAddressLinkName("Address Information");
...................................................
|
- 在正确的作用域内存储该表单 bean:
try
{
//Store the bean in the correct scope
if ("request".equals(mapping.getScope()))
request.setAttribute(mapping.getName(), formBean);
else
session.setAttribute(mapping.getName(), formBean);
}
catch (Exception e)
{
..................................
}
|
- 在
overview/struts-config.xml
中查找映射并返回一个 ActionForward
类型的对象:
String forwardName = null;
ActionForward forward = new ActionForward();
.......................................
//If no errors occur then look for "success"
try
{
forward = mapping.findForward(forwardName);
if (forward != null)
{
.....................................
}
else
.....................................
}
catch(Exception e)
{
...................................
}
return (forward);
|
- 如果在操作类的处理过程中没有出现错误,那么根据我们在
overview/struts-config.xml
文件中的配置,转发名称 success
会允许启动 Overview.jsp
。
Overview.jsp
显示两个链接,指向已开发的其他 portlet——Receive Phone Numbers Request Struts Portlet 和 Receive Address Request Struts Portlet。我们使用页面导航方法和 Struts 标记来实现这一特征:
- 在 JSP 页面的最开始的地方使用
taglib
指示来使所有的 engine.tld
标记可用于剩余的带有 prefix.wps
的 JSP 页面。
<%@ taglib uri="/WEB-INF/tld/engine.tld" prefix="wps" %>
|
- 在
<wps:urlGeneration>
portlet 标记中使用 contentNode
参数来指定驻留其他 portlet 的页面的惟一标识符或自定义名称。为了启动到 Receive Address Request Struts Portlet(位于 Struts_Receiver 页面)的页面导航,我们使用了自定义名称 Struts_Receiver_Page
:
<wps:urlGeneration contentNode="Struts_Receiver_Page">
<A HREF="<%wpsURL.write(out);%>"style="text-decoration:none" >
<bean:write name="overviewFormBean" property="addressLinkName" /></a>
</wps:urlGeneration>
|
- 为了启动到 Receive Phone Numbers Request Struts Portlet(位于
Struts_Receiver_2
页面)的页面导航,我们使用自定义名称 Struts_Receiver_2_Page
:
<wps:urlGeneration contentNode="Struts_Receiver_2_Page">
<A HREF="<%wpsURL.write(out);%>"style="text-decoration:none" >
<bean:write name="overviewFormBean"
property="phoneNumbersLinkName" /></a>
</wps:urlGeneration>
|
- 这样我们就从 My Pages 和惟一名称列表获得了页面的自定义名称,如图 2 所示。
- 在
overview
模块中,我们使用 <bean:write>
、<html:html>
和 <html:form>
Struts 标记。实现这些标记需要以下的步骤:
- 声明
html
和 bean
前缀:
<%@ taglib uri="/WEB-INF/tld/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/tld/struts-bean.tld" prefix="bean" %>
|
<html:html>
Struts 标记:如果在用户会话中有可用的当前地点,就会以适当的语言属性来呈现 HTML 元素。
<html:form>
Struts 标记:用于在 Struts 中的所有 HTML 表单处理。form
元素必须始终包含 action
属性。action 属性必须符合 struts-config.xml
中的有效操作。在 Overview.jsp
文件中,我们使用 form
元素和它的 action
属性:
<html:form action="/start">
|
start
属性映射 overview/struts-config.xml
文件中的下列操作:
<action-mappings>
<action path="/start"
type="com.test.strutstest.actions.OverviewAction"
name="overviewFormBean"
scope="request">
<forward name="success" path="/Overview.jsp"/>
</action>
</action-mappings>
|
Struts 标记将表单字段和 Struts 中的表单 bean 连结在一起。根据用于相关联的 ActionMapping 的表单 bean 规范定位(或生成,如果有必要)表单 bean。我们在 overview/struts-config.xml
文件中定义的表单 bean 为 UserFormBean
:
<form-beans>
<form-bean name="overviewFormBean" type="com.test.strutstest.forms.OverviewFormBean"/>
</form-beans>
|
<bean:write>
Struts 标记的使用:
这一通用标记用于从 bean 输出特性值。在 Overview.jsp
文件中,它显示 <bean:write>
来显示在 UserFormBean
表单 bean 中设置的文本数据。我们使用这一标记来显示 UserFormBean
的某些特性,如下所示:
<bean:write name="overviewFormBean" property="addressLinkName" />
<bean:write name="overviewFormBean" property="phoneNumbersLinkName" />
|
开发其余的模块
开发 address
和 phone-numbers
模块与开发 overview
模块非常类似,不同之处在于:
address
模块实现 Receive Address Request Struts Portlet 使用的是:
address.jsp
- DisplayAddressInfoAction class
- UserFormBean form bean class
- UserBean backend bean class
- UserBeanHelper model class
address/struts-config.xml
file.
phone-numbers
模块实现 Receive Phone Numbers Request Struts Portlet 使用的是:
phone-numbers.jsp
- DisplayPhoneInfoAction class
- UserFormBean form bean class
- UserBean backend bean class
- UserBeanHelper model class
phone-numbers/struts-config.xml.
对于 address
和 phone-numbers
模块,我们使用的都是后端 UserBean bean 类和 UserBeanHelper 模型类,这两个类都需要使用存根数据来模拟用户信息,而存根数据是使用我们的模块类在后端 bean 中设置的。这两个模块都使用同样的表单 bean。
要在 DisplayAddressInfoAction 类中开发 address
模块需要做的是:
一旦控制权从 default
模块转移到 address
模块,DisplayAddressInfoAction 类的 execute() 方法就会执行以下任务:
- 访问表单 bean——
UserFormBean
,并设置以下属性:
try
{
String forwardName = null;
if (form == null)
{
form = (ActionForm) request.getAttribute("userFormBean");
}
//Set-up form bean fields
UserFormBean formBean = (UserFormBean) form;
setFormBean(formBean);
...................................................
|
- 在正确的作用域中存储该表单 bean:
try
{
........................................
//Store the bean in the correct scope
if ("request".equals(mapping.getScope()))
request.setAttribute(mapping.getName(), formBean);
else
session.setAttribute(mapping.getName(), formBean);
}
catch (Exception e)
{
..................................
}
|
- 在
address/struts-config.xml