2. 第一个示例
我们的大多数示例将仅限于使用 Struts 2 实现的 Web 层:
![]() |
在掌握基础知识后,我们将探讨一个采用多层架构的更复杂的示例。
2.1. 生成示例
我们将构建我们的第一个应用程序。
![]() |
- 在 [1] 中,我们创建一个新项目
- 在 [2] 中,选择 Java Web / Web 应用程序类型
- 在 [3] 中,为项目命名
- 在 [4] 中,指定项目位置。
- 在 [5] 中,我们将新项目设为主项目。
![]() |
- 在 [6] 中,选择 Tomcat 服务器。安装 NetBeans 7.01 时,会同时安装两个服务器:Apache Tomcat 和 GlassFish 3.1。
- 在 [7] 中,指定将使用 Struts 2 框架。之所以提供此选项,是因为已安装了 Struts 2 插件。若未安装该插件,则不会提供 Struts 2 框架。
- 在 [8] 中,我们请求创建我们将要学习的示例项目。
- 在 [9] 中,您可以确认将使用哪些 Struts 2 库。
![]() |
- 在[10]中,是生成的项目。我们稍后将回到这里。
- 在 [11] 中,是项目库。它们由 Struts 2 插件自动集成。若您未安装该插件,可在下载的 Struts 2 发行版中的 [lib] 文件夹内找到这些库。随后请按照步骤 12 和 13 进行操作。
2.2. 文件系统中的生成的项目
![]() |
- 在 [1] 中,[Projects] 选项卡显示了项目的“开发者”视图
- 在 [2] 中,[Files] 选项卡显示了文件系统中的项目文件夹
- 在 [2A] 中,[Web Pages] 分支在 [2] 中由 [web] 文件夹 [2B] 表示
- 在 [3A] 中,[2] 中的 [Source Packages] 分支由 [java] 文件夹 [3B] 表示
2.3. 配置文件 [META-INF/context.xml]
![]() |
该文件内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<Context antiJARLocking="true" path="/exemple-01"/>
第 2 行表明 Web 应用程序上下文为 /example-01。所有形式为 [http://machine:port/exemple-01/...] 的 URL 都将由该应用程序处理。该上下文可在项目属性中找到 [2]:右键单击项目 / 属性 / 运行。
2.4. 配置文件 [WEB-INF/web.xml]
![]() |
每个 Web 应用程序都通过应用程序 [WEB-INF] 文件夹中的 [web.xml] 文件进行配置。生成的文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>example/HelloWorld.jsp</welcome-file>
</welcome-file-list>
</web-app>
- 第 3–6 行定义了一个由 Struts 2 类 [org.apache.struts2.dispatcher.FilterDispatcher] 实现的过滤器。该类在 MVC 模型中充当控制器(C)。
- 第 7–10 行:定义了 URL 模式与必须处理匹配该模式的 URL 的过滤器之间的映射。此处指定任何 URL(模式 /*)都必须由名为 struts2 的过滤器处理。这是第 3–6 行中定义的过滤器。因此,所有 URL 都将通过 Struts 2 控制器。
- 第 11–15 行:定义用户会话的持续时间,此处设置为 30 分钟。 在处理各种请求时,系统会通过会话令牌来追踪用户。该令牌在用户首次请求时分配,随后用户会在每次新请求中系统性地发送该令牌。这使得 Web 服务器能够识别用户,并为其管理一个称为“会话”的“记忆”。如果两次请求之间间隔超过 30 分钟,系统将为用户生成一个新的会话令牌,用户因此会丢失其“记忆”并开始一个新的会话。
- 第16–18行:定义当用户访问Web应用程序但未请求文档时显示的文件。因此,当请求的URL为[http://machine:port/exemple-01]时,返回的URL将为[http://machine:port/exemple-01/example/HelloWord.jsp]。
2.5. [struts.xml] 配置文件
![]() |
[struts.xml] 文件是 Struts 2 的配置文件。它可以位于项目 ClassPath 中的任意位置。在上述 NetBeans 项目中,项目的 ClassPath 由两个分支组成:
- 源代码包
- 库
因此,位于这两个分支中的任何文件夹或库都属于项目的 ClassPath 范围。通常将 [struts.xml] 放置在 <default package> 中。此处的文件内容如下:
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<include file="example.xml"/>
<!-- Configuration for the default package. -->
<package name="default" extends="struts-default">
</package>
</struts>
- 第 5 行和第 10 行:文档的根标签是 <struts> 标签
- 第 6 行:此处插入了 [example.xml] 文件。因此它带来了自身的配置。我们稍后会再讨论这一点。
- 第 8–9 行:在此处定义了一个名为“default”的包。包允许您配置一组具有相同 URL 的 Struts 2 动作。例如,[/path/Action1] 和 [/path/Action2]。然后,您可以为这些动作定义一个包:
<package name="employes" namespace="/employes" extends="struts-default">
... configuration
</package>
上面的包名为“employees”,用于配置 URL 为 /employees/Action 的 Action。包可以通过“extends”关键字继承自另一个包。在上例中,“employees”包继承自“struts-default”包。该包位于 struts2-core.jar 库中的 [struts-default.xml] 文件内:
![]() |
在 [struts-default.xml] 文件中定义的“struts-default”包配置了各种元素,包括在调用操作时执行的拦截器列表。让我们回到 Struts 2 应用程序的 MVC 结构:
![]() |
要处理形式为 [http://machine:port/.../Action] 的 URL,[FilterDispatcher] 控制器将实例化实现所请求操作的类,并执行该类的一个方法——默认情况下是名为 execute 的方法。对该 execute 方法的调用将经过一系列拦截器:
![]() |
拦截器和操作都会处理同一个请求。大多数情况下,struts-default 包中定义的拦截器列表已经足够。因此,我们的 Struts 包将始终继承 struts-default 包。要了解各种拦截器的作用,请参阅 [ref2] 的第 4 章。
让我们回到 struts.xml 配置文件:
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<include file="example.xml"/>
<!-- Configuration for the default package. -->
<package name="default" extends="struts-default">
</package>
</struts>
- 第 8 行:名为“default”的包具有特殊作用。它处理其他包中未配置的操作。在此,第 8–9 行中未为“default”包指定任何配置。因此,我们可以删除该包的定义。
现在让我们看看 [struts.xml] 文件第 6 行中包含的 [example.xml] 文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<package name="example" namespace="/example" extends="struts-default">
<action name="HelloWorld" class="example.HelloWorld">
<result>/example/HelloWorld.jsp</result>
</action>
</package>
</struts>
- 第 8 行:定义了一个名为 example 的包,该包继承自 struts-default 包。该包处理 /example/Action 类型的 URL(命名空间 /example)。
- 第 9–11 行:定义了一个名为 HelloWorld 的 Action(name 属性),对应 URL /example/HelloWorld。该 Action 由 example.HelloWorld 类的实例处理(class 属性)。
- [FilterDispatcher] 控制器将执行该类的 execute 方法。
- 该方法将返回一个称为导航键的字符串。
- 各种导航键必须使用 <result name="key"/> 标签进行定义(第 10 行)。如果省略 name 属性,则默认使用 success 键。上文即为这种情况。因此,example.HelloWorld 类的 execute 方法必须向 [FilterDispatcher] 控制器返回 success 键。
- 随后,[FilterDispatcher] 控制器将显示页面 [/example/HelloWorld.jsp](第 10 行)。
如果我们将 [struts.xml] 和 [example.xml] 这两个文件合并,并移除看似多余的默认包,就相当于将 [struts.xml] 文件简化为仅包含 [example.xml] 文件。
2.6. HelloWorld 动作
![]() |
根据我们分析过的 [struts.xml] 文件,当客户端请求的 URL 为 /example/HelloWorld 时,HelloWorld 动作会被触发。随后其 execute 方法将被执行。该方法必须返回一个导航键。我们看到只有一个导航键:success,并且系统会将页面 /example/HelloWorld.jsp 作为响应发送给用户。
HelloWorld 动作的代码如下:
package example;
import com.opensymphony.xwork2.ActionSupport;
public class HelloWorld extends ActionSupport {
public String execute() throws Exception {
setMessage(getText(MESSAGE));
return SUCCESS;
}
public static final String MESSAGE = "HelloWorld.message";
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
- 第 5 行:HelloWorld 类继承了 Struts 2 的 ActionSupport 类。这种情况几乎总是如此。这使我们能够使用某些方法,例如第 8 行中的 getText 方法。
- 第 7–10 行:`execute` 方法,该方法在调用 Struts 控制器时执行。我们知道它必须返回一个字符串,因此第 7 行定义了其方法签名。此处,它返回 `SUCCESS` 常量,该常量也在 `ActionSupport` 中定义。`execute` 方法的其他返回结果也以相同方式定义了常量:
SUCCESS | "success" |
错误 | "错误" |
输入 | "输入" |
登录 | "登录" |
因此,此处的 execute 方法返回字符串 "success"。根据 [struts.xml] 文件,这意味着页面 /example/HelloWorld.jsp 将返回给用户。
- 第 8 行:execute 方法在第 14 行将 message 字段初始化为 getText("HelloWorld.message") 的值。getText 方法属于父类 ActionSupport。它会根据当前使用的语言从文件中获取文本。默认情况下,此处将使用位于与该操作相同包中的 package.properties 文件。该文件有两个版本:
[package.properties] 文件内容如下:
该文件由一系列采用 key=value 格式的文本行组成。如果使用此文件,getText("HelloWorld.message") 将返回值 Struts 已启动并运行 ...
[package_es.properties] 文件内容如下:
如果客户端浏览器使用的语言是西班牙语(即 package_es.properties 文件中的 "es" 属性),getText("HelloWorld.message") 将返回值 ¡Struts está bien! ... 在所有其他情况下,将使用 [package.properties] 文件。
2.7. HelloWorld.jsp 视图
![]() |
这是 Struts 拼图的最后一块。当请求 URL /example/HelloWorld 时,显示的就是这个视图。我们已经看到了初始请求最终显示此响应所经过的路径。页面代码如下:
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title><s:text name="HelloWorld.message"/></title>
</head>
<body>
<h2><s:property value="message"/></h2>
<h3>Languages</h3>
<ul>
<li>
<s:url id="url" action="HelloWorld">
<s:param name="request_locale">en</s:param>
</s:url>
<s:a href="%{url}">English</s:a>
</li>
<li>
<s:url id="url" action="HelloWorld">
<s:param name="request_locale">es</s:param>
</s:url>
<s:a href="%{url}">Espanol</s:a>
</li>
</ul>
</body>
</html>
- 该页面使用了 HTML 标签(第 5、6 行等)以及第 3 行定义的库中的标签。所有 <s:xx> 标签都属于该库。
- 第 7 行:<s:text> 标签允许您根据客户端浏览器的语言显示不同的文本。name 属性指定了在消息文件中要查找的键。这里同样会使用 package_xx.properties 文件。请注意,这些文件仅包含一个键为 HelloWorld.message 的消息。
- 第 11 行:<s:property name="property"> 标签用于写入名为 ActionContext 的对象的属性值。该对象包含:
- 已执行操作的属性。name="message" 将显示当前操作的 message 字段的值。与该字段关联的 get 和 set 方法用于获取其值或对其进行初始化。因此,这些方法必须存在。
- 当前请求的属性,表示为 <s:property name="#request['key']">
- 用户会话的属性,表示为 <s:property name="#session['key']">
- 应用程序本身的属性,表示为 <s:property name="#application['key']">
- 客户端浏览器发送的参数,表示为 <s:property name="#parameters['key']">
- <s:property name="#attr['key']"> 这种表示法将按页面、请求、会话和应用程序的顺序,依次显示在这些对象中搜索到的值。
- 第 16–18 行:<s:url> 标签用于定义 URL。id 属性为将要创建的 URL 指定一个名称。该名称随后在第 19 行中使用。action 属性指定 URL 应指向哪个操作。
- 第 17 行:<s:param ..> 标签允许您以 ?param1=value1¶m2=value2&... 的形式向 URL 中添加参数。此处添加的参数为 ?request_locale=es。
最终生成的 URL 将如下所示:
要理解这个 URL,请记住 [HelloWorld.jsp] 页面会在以下两种情况下显示:
- 直接请求 URL [/example-01/example/HelloWorld.jsp] 时
- 当请求操作 [/example-01/example/HelloWorld.action] 时
在这两种情况下,URL 路径均为 /example-01/example。<s:url action= "... "> 标签会将 action 属性定义的操作追加到此路径上,从而生成 /example-01/example/HelloWorld。随后,它会将 .action 后缀以及(如有)任何 URL 参数追加到前面的 URL 上。 生成的 URL /example-01/example/HelloWorld.action?request_locale=en 将调用 [struts.xml] 文件中定义的 HelloWorld 操作,同时传递参数 request_locale=en。该参数不会由 HelloWorld 操作处理,而是由 Struts 的拦截器之一处理,具体而言是负责页面国际化的那个拦截器。 系统将识别并处理 request_locale 参数,页面语言将切换为英语 (en)。
- 第 19 行:定义了一个 HTML 链接。<a> 标签的 href 属性期望接收一个字符串。在此,我们希望使用第 16 行中 id 为 "url" 的 URL 的值。为此,我们写 href="%{url}"。url 变量将被求值,其值将赋给 href 属性。在大多数情况下,变量求值是隐式的。例如,当我们写
<s:property name="message"/>
时,显示的是 message 属性的值,而不是字符串 "message"。但在其他情况下,你必须强制求值变量或属性。如果我们写成 href="url",字符串 "url" 就会被赋给 href 属性。
- 第 23–27 行:创建一个 HTML 链接,用于将页面语言切换为西班牙语。
2.8. 运行应用程序
我们启动该项目:
![]() |
- 在 [1] 中,我们运行 [example-01] 项目。如果 Tomcat Web 服务器尚未启动,则会自动启动。随后请求 URL [/example-01] [3]。此时将使用 [web.xml] 文件:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>example/HelloWorld.jsp</welcome-file>
</welcome-file-list>
</web-app>
- 由于请求的 URL [/example-01] 未指定具体页面,Tomcat 将使用第 16 行和第 18 行中的 <welcome-file-list> 标签。因此,将返回 URL /example-01/example/HelloWorld.jsp。
- 由于 Struts 2 会处理所有 URL(第 8 行和第 9 行),该 URL 将被 Struts 过滤。由于它不对应于一个 Action,而是对应于一个 JSP 页面,因此将显示该 JSP 页面。
- 因此,我们在 [2] 中看到的就是我们所研究的 HelloWorld.jsp 页面。
- 在 [4] 中,我们可以看到 <title><s:text name="HelloWorld.message"/></title> 标签未生效,<h2><s:property value="message"/></h2> 标签同样未生效。原因是未调用任何 Action。 因此,在动作执行前运行的拦截器列表并未被执行,特别是负责国际化的那个。国际化标签 <s:text ...> 无法被正确处理。此外,引用 Action1 类中 message 字段的 message 属性并不存在。因此导致内容未显示。
现在,让我们点击 [English] 链接。我们会看到以下页面:
![]() |
- 在 [1] 中,即请求的 URL。我们已解释过该 URL 的构成方式。此次请求的是一个 Struts 操作:即在 [example.xml] 中定义的 HelloWorld 操作。
<struts>
<package name="example" namespace="/example" extends="struts-default">
<action name="HelloWorld" class="example.HelloWorld">
<result>/example/HelloWorld.jsp</result>
</action>
</package>
</struts>
- 该动作的 execute 方法已执行。我们看到它返回了键值“success”。从上面的第 4 行可以推断,页面 /example/HelloWorld.jsp 被返回给客户端。这就是我们在 [3] 中看到的内容。
- 在[1]中,我们可以看到请求的URL由参数`request_locale=en`设定。页面语言现在将设为英语。实际上,该语言选择会被存储在用户的会话中,并一直保留该选择,直到用户更改为止。
- 在[2]和[3]中,我们可以看到国际化功能的实际应用。此次,标签 <title><s:text name="HelloWorld.message"/></title> 以及 <h2><s:property value="message"/></h2> 已生效。
如果现在选择 [Espanol] 链接,我们将看到西班牙语版本的页面:
![]() |
2.9. 结论
我们分析了一个由 NetBeans 的 Struts 2 插件自动生成的示例。我们发现,要追踪请求的处理过程相当困难。其中涉及以下要素:
- 配置文件 [web.xml]、[struts.xml]
- 执行的操作:拦截器与 execute 方法。
起初,Struts 2 的工作原理可能显得复杂,需要一些时间来适应。接下来我们将展示一系列示例,每个示例都将阐释 Struts 的某个具体方面。















