9. 示例 07 – 表单标签
我们将构建并使用以下表单:


我们将把文本输入字段中看到的参数注入概念扩展到表单中的其他 HTML 元素。
9.1. NetBeans 项目
![]() |
- 在 [1] 中,项目视图 [Formx.jsp]
- 在 [2] 中,Struts 配置文件和国际化消息
- ,以及 [3] 中的动作 [Formx.java] 和另一个 Struts 配置文件。
9.2. Struts 配置
[struts.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>
<constant name="struts.custom.i18n.resources" value="messages" />
<include file="example/example.xml"/>
<package name="default" namespace="/" extends="struts-default">
<default-action-ref name="index" />
<action name="index">
<result type="redirectAction">
<param name="actionName">Form</param>
<param name="namespace">/example</param>
</result>
</action>
</package>
</struts>
- 第 9 行:引入另一个配置文件 [example.xml]。该文件用于配置动作。
- 第 11–19 行:默认包。定义了 URL / 的重定向地址。该 URL 将被重定向至 /example/Form.action。
用于配置操作的 [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="Form" class="example.Form">
<result name="success">/example/Form.jsp</result>
</action>
<action name="Form1" class="example.Form1">
<result name="success">/example/Form1.jsp</result>
</action>
<action name="Form2" class="example.Form2">
<result name="success">/example/Form2.jsp</result>
</action>
</package>
</struts>
- 第 7–17 行:定义操作 /example/Form、/example/Form1、/example/Form2,其中 /example 是命名空间(第 7 行),Formx 是操作。
- 第 8–10 行:执行 Form 操作将显示 /example/Form.jsp 视图。
- 第 11–13 行:执行 Form1 操作将显示 /example/Form1.jsp 视图。
- 第 14–16 行:执行 Form2 操作将显示 /example/Form2.jsp 视图。
9.3. 消息文件
我们不会过多讨论项目的国际化问题。我们在示例 01 中已经涵盖了这一内容。我们提供 [messages.properties] 文件的内容,以便读者能够理解后续的视图。
Form.francais=Fran\u00E7ais
Form.anglais=Anglais
Form.titre=Struts 2 - les tags de formulaire
Form.message=Struts 2 - les tags de formulaire
Form.langues=langues
Form.textfield=1-textfield
Form.password=2-password
Form.textarea=3-textarea
Form.select1=4-select (multiple=false, size=1)
Form.select1.header=<-- select1 -->
Form.select2=5-select (multiple=false, size=3)
Form.select3=6-select (multiple=true, size=3)
Form.radio=7-radio
Form.checkbox=8-checkbox
Form.checkboxlist=9-checkboxlist
Form.hidden=10-hidden
Form.submitText=Valider
Form.buttonRazText=Raz
Confirmation.message=Confirmation des valeurs saisies
Confirmation.champ=champ
Confirmation.valeur=valeur
Confirmation.textfield=1-textfield
Confirmation.password=2-password
Confirmation.textarea=3-textarea
Confirmation.select1=4-select (multiple=false, size=1)
Confirmation.select2=5-select (multiple=false, size=3)
Confirmation.select3=6-select (multiple=true, size=3)
Confirmation.radio=7-radio
Confirmation.checkbox=8-checkbox
Confirmation.checkboxlist=9-checkboxlist
Confirmation.hidden=10-hidden
9.4. [Form.jsp] 视图 – 输入部分
其外观如下:

具体如下:
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title><s:text name="Form.titre"/></title>
<s:head/>
</head>
<body background="<s:url value="/ressources/standard.jpg"/>">
<h2><s:text name="Form.message"/></h2>
<h3><s:text name="Form.langues"/></h3>
<ul>
<li>
<s:url id="url" action="Form">
<s:param name="request_locale">en</s:param>
</s:url>
<s:a href="%{url}"><s:text name="Form.anglais"/></s:a>
</li>
<li>
<s:url id="url" action="Form">
<s:param name="request_locale">fr</s:param>
</s:url>
<s:a href="%{url}"><s:text name="Form.francais"/></s:a>
</li>
</ul>
<s:form name="formulaire">
<s:textfield name="textfield" key="Form.textfield" />
<s:password name="password" key="Form.password"/>
<s:textarea name="textarea" key="Form.textarea" cols="40" rows="5"/>
<s:select name="select1" list="select1Values" size="1" key="Form.select1" headerValue="<-- select 1 -->" headerKey="-1" />
<s:select name="select2" size="3" list="select2Values" key="Form.select2"/>
<s:select name="select3" size="3" list="select3Values" key="Form.select3" multiple="true"/>
<s:radio name="radio" list="radioValues" key="Form.radio"/>
<s:checkbox name="checkbox" key="Form.checkbox"/>
<s:checkboxlist name="checkboxlist" list="checkboxlistValues" key="Form.checkboxlist"/>
<s:hidden name="hidden" key="Form.hidden"/>
<s:submit key="Form.submitText" name="submitText"/>
</s:form>
<hr/>
...
</body>
</html>
- 表单从第 26 行开始,到第 38 行结束。在第 26 行,缺少 `action` 属性。该属性指定表单数据将提交到的操作。如果缺少 `action` 属性,数据将提交到导致该视图显示的操作,在本例中即 `[Form]` 操作。
- 第 27 行:一个输入字段。key 属性指定其标签。name 属性是将被提交的参数名称。该字段提交的字符串将采用以下形式:
textfield=text_entered
该参数名称对应于 [Form] 操作中的一个字段。text_saisi 输入字段的值将被注入到 [Form] 操作的 textfield 字段中。 反之,如果 [Form.jsp] 视图在 [Form] 操作之后显示,该输入字段的值将取自 [Form] 操作中的 textfield 字段。因此,该字段既用于读取也用于写入。为此,[Form] 类必须提供 getTextField 和 setTextField 方法。
这一原理适用于后续的所有 input 标签,此处不再赘述。
- 第 28 行:密码输入。提交至 [Form] 操作的字符串将采用以下形式
password=entered_password
[Form] 操作中必须存在一个带有 get/set 方法的密码字段。
- 第 29 行:在 5 行 40 列的区域中输入多行文本。提交到 [Form] 操作的字符串将采用以下形式
textarea=输入内容
[Form] 动作中必须存在一个带有 get/set 方法的 textarea 字段。
- 第 30 行:单选下拉列表(size=1)。列表中只能选择一个值。提交到 [Form] 操作的字符串将采用以下形式:
select1=selected_value
[Form] 动作中必须存在一个带有 get/set 方法的 select1 字段。
key 属性是列表的标签。list 属性定义了要放入下拉列表框中的值列表。在此,将调用 [Form] 操作的 getSelect1Values 方法来填充下拉列表框。该方法可以返回集合、字典或数组。在此,它将返回一个字符串数组 [string1, string2, ...],这将生成以下 HTML 代码:
<option value="string1">string1</option>
<option value="string2">string2</option>
...
位于 <option>...</option> 标签之间的文本将显示在下拉列表中。value 属性指定了当用户选中该选项时要提交的值。在此示例中,value 属性与下拉列表中显示的文本相同。但通常情况下并非如此。如果选中了第二个选项,则会向 [Form] 操作提交以下字符串:
select1=string2
此时 [Form].select1 字段将接收值 string2。反之,如果表单显示时该字段被设置为 string3,那么从视觉上看,第三个元素
<option value="string3">string3</option>
将显示为已选中状态。
headerValue 属性用于设置下拉列表中作为第一个选项显示的文本,而 headerKey 属性则用于设置当用户选中该选项时提交的值。
- 第 31 行:同样是一个大小为 3 的列表(size=3):与之前仅显示一个项目的列表不同,这里将显示列表中的 3 个项目。选择模式保持不变。只能选择一个项目。
- 第 32 行:另一个列表,但这次支持多选(multiple=true)。在这种情况下,提交到 [Form] 操作的值将采用以下形式:
select3=string2&select3=string5
(前提是选项 string2 和 string5 已被选中)。当同一参数(此处为 select3)收到多个值时,该操作必须包含一个与参数同名的字段,且该字段类型为数组。因此,例如,此处的 [Form] 操作可以包含如下字段:
String[] select3;
以及相应的 get/set 方法。此时,select3 数组将接收字符串数组 [string2, string5]。反之,当显示 [Form.jsp] 视图时,[Form].select3 字段的值将决定 select3 下拉列表中哪些选项显示为已选中。这始终是一项读写操作。
- 第 33 行:一组单选按钮。组标签由 key 属性提供。各个按钮的标签由 list 属性提供。 该属性的值是 [Form] 动作中某个方法的名称,该方法必须返回一个集合、字典或数组。在此,[Form].getRadioValues 方法将返回一个字符串数组 [string1, string2, ...],该数组将生成以下 HTML 标签:
<input type="radio" name="radio" ... value="string1"/>string1
<input type="radio" name="radio" ... value="string2"/>string2
...
如果选中了 radio 按钮 string2,提交的值将是
radio=string2
,该值将被插入到 [Form].radio 字段中。
- 第 34 行:一个复选框。如果选中,则会将以下参数字符串提交到 [Form] 操作
checkbox=true
如果未勾选此选项,则不会提交任何参数字符串。Struts 2 具有一种内部机制,使其运行效果如同该字符串
checkbox=false
已提交。
- 第 35 行:一组复选框。可以同时选中多个复选框。组标签由 key 属性提供。各个复选框的标签由 list 属性提供。 该属性的值是 [Form] 动作中某个方法的名称,该方法必须返回一个集合、字典或数组。在此,[Form].getCheckboxlistValues 方法将返回一个字符串数组 [string1, string2, ...],该数组将生成以下 HTML 标签:
<input type="checkbox" name="checkboxlist" ... value="string1"/>string1
<input type="checkbox" name="checkboxlist" ... value="string2"/>string2
...
如果用户选中了标有 string2 和 string5 的复选框,则会向 [Form] 操作提交以下字符串:
checkboxlist=string2&checkboxlist=string5
数组 [string2, string5] 将被赋值给 [Form].checkboxlist 字段,因此该字段必须是字符串数组类型,这与多选列表的情况相同。反之,当显示 [Form.jsp] 视图时,[Form].checkboxlist 字段的值将决定哪些复选框会被选中。
- 第 36 行:一个隐藏字段。隐藏字段是指无需用户输入即可提交的字段。其值由程序员设定。在此,该隐藏字段将从 [Form].hidden 字段获取其值。该值将以以下形式提交:
hidden=something
并重新赋值给 [Form].hidden 字段。
- 第 37 行:一个提交按钮。点击此按钮将把所有表单值提交至 [Form] 操作。提交的参数字符串将如下所示:
textfield=text&password=&textarea=line1%0D%0Aline2%0A&select1=zero&select2=three&select3=zero&select3=two&__multiselect_select3=&radio=blue&checkbox=true&__checkbox_checkbox=true&checkboxlist=v%C3%A9lo&checkboxlist=bus&__multiselect_checkboxlist=&hidden=initial&submitText=Validate
9.5. [表单] 操作 – 输入部分
其代码如下:
package example;
import com.opensymphony.xwork2.ActionSupport;
public class Form extends ActionSupport {
// constructor without parameters
public Form() {
}
// form fields
private String textfield = "texte";
private String password = "secret";
private String textarea = "ligne1\nligne2\n";
private String select1 = "zéro";
private String[] select1Values = new String[]{"zéro", "un", "deux"};
private String select2 = "trois";
private String[] select2Values = new String[]{"zéro", "un", "deux", "trois", "quatre"};
private String[] select3 = new String[]{"zéro", "deux"};
private String[] select3Values = new String[]{"zéro", "un", "deux", "trois", "quatre"};
private String radio = "bleu";
private String[] radioValues = new String[]{"bleu", "blanc", "rouge"};
private Boolean checkbox = false;
private String[] checkboxlist = new String[]{"vélo", "bus"};
private String[] checkboxlistValues = new String[]{"voiture", "tram", "vélo", "bus", "métro"};
private String hidden = "initial";
private String submitText;
// values selected in select3 field
public String getSelect3SelectedValues() {
return getValue(select3);
}
// values selected in checkboxlist field
public String getCheckboxlistSelectedValues() {
return getValue(checkboxlist);
}
// utilitarian method
public String getValue(String[] values) {
String result = "";
for (String value : values) {
result += " " + value;
}
return result;
}
// field getters and setters
....
}
- 第 1 行:与表单的 textfield 标签关联的字段。用于初始化此输入字段以及获取其值。接收在 POST 请求时此字段中输入的值。
- 第 2 行:与表单的 password 标签关联的字段。接收 POST 请求期间在此字段中输入的值。
- 第 13 行:与表单的 textarea 标签相关联。接收 POST 请求期间在此字段中输入的文本。
- 第 14 行:与表单的 `select1` 标签相关联,这是一个单选列表。其初始值用于从下拉列表中选择一个项目。`value` 属性与该值匹配的项目将被选中。在 POST 请求时,接收下拉列表中选定选项的 `value` 属性。
- 第 15 行:将填充 `select1` 下拉列表的字符串。
- 第 16 和 17 行:与 select1 相同
- 第 18 行:与表单的 `select1` 标签关联,这是一个多选列表。数组中的初始值将选中列表中的对应元素。其 `value` 属性与这些值之一匹配的元素将被选中。在 POST 请求时,它将接收下拉列表中选中各项的 `value` 属性。
- 第 19 行:用于填充 select3 下拉列表的字符串
- 第 20 行:与表单的 radio 标签相关联。其值用于选中一个单选按钮——即 value 属性与该值匹配的按钮。在 POST 请求期间接收所选单选按钮的值。
- 第 21 行:单选按钮的各种标签和值。
- 第 22 行:与表单的 checkbox 标签相关联。其初始值用于勾选或取消勾选该复选框。在 POST 请求时,如果复选框被勾选,则接收值 true,否则接收值 false。
- 第 23 行:与表单的 checkboxlist 标签相关联。数组中的初始值用于选中相应的复选框。其 value 属性与这些初始值之一匹配的复选框将被选中。在 POST 请求时,接收各个已选中复选框的 value 属性值。
- 第 24 行:用于填充 checkboxlist 中复选框标签的字符串。
- 第 25 行:与隐藏字段相关联。其初始值用于设置隐藏字段的值。在 POST 请求时,它将接收该相同值,因为用户无法修改它。
- 第 26 行:与 submit 标签相关联。在 POST 请求期间,它将接收按钮的标签。该字段是可选的。
- 第 29、34 和 39 行中的方法将在下文进一步讨论。
9.6. [Form.jsp] 视图 – 确认部分
[Form.jsp] 视图中有一个尚未介绍的“输入确认”部分:

其代码如下:
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
...
<hr/>
<h2><s:text name="Confirmation.message"/></h2>
<table border="1">
<tr>
<th><s:text name="Confirmation.champ"/></th>
<th><s:text name="Confirmation.valeur"/></th>
</tr>
<tr>
<td><s:text name="Form.textfield"/></td>
<td><s:property value="textfield"/>
</tr>
<tr>
<td><s:text name="Form.password"/></td>
<td><s:property value="password"/>
</tr>
<tr>
<td><s:text name="Form.textarea"/></td>
<td><s:property value="textarea"/>
</tr>
<tr>
<td><s:text name="Form.select1"/></td>
<td><s:property value="select1"/>
</tr>
<tr>
<td><s:text name="Form.select2"/></td>
<td><s:property value="select2"/>
</tr>
<tr>
<td><s:text name="Form.select3"/></td>
<td><s:property value="select3SelectedValues"/>
</tr>
<tr>
<td><s:text name="Form.radio"/></td>
<td><s:property value="radio"/>
</tr>
<tr>
<td><s:text name="Form.checkbox"/></td>
<td><s:property value="checkbox"/>
</tr>
<tr>
<td><s:text name="Form.checkboxlist"/></td>
<td><s:property value="checkboxlistSelectedValues"/>
</tr>
<tr>
<td><s:text name="Form.hidden"/></td>
<td><s:property value="hidden"/>
</tr>
</table>
</body>
</html>
- 第 13 行:显示 [Form].textfield 字段的值
- 第 17 行:显示 [Form].password 字段的值
- 第 21 行:显示 [Form].textarea 字段的值
- 第 25 行:显示 [Form].select1 字段的值
- 第 29 行:显示 [Form].select2 字段的值
- 第 33 行:显示 [Form].select3 数组的值。为此,它使用了 [Form].getSelect3SelectedValues 方法。
- 第 37 行:显示 [Form].radio 字段的值
- 第 41 行:显示 [Form].checkbox 字段的值
- 第 45 行:显示 [Form].checkboxlist 数组的值。为此,它使用了 [Form].getCheckboxlistSelectedValues 方法。
- 第 49 行:显示 [Form].hidden 字段的值
9.7. [Form] 操作 – 确认部分
上述的 getSelect3SelectedValues 和 getCheckboxlistSelectedValues 方法定义在 [Form] 类中:
// valeurs sélectionnées dans champ select3
public String getSelect3SelectedValues() {
return getValue(select3);
}
// valeurs sélectionnées dans champ checkboxlist
public String getCheckboxlistSelectedValues() {
return getValue(checkboxlist);
}
// méthode utilitaire
public String getValue(String[] values) {
String result = "";
for (String value : values) {
result += " " + value;
}
return result;
}
它们只是将需要显示的数组元素进行拼接。
9.8. 测试
运行 NetBeans 项目时,将显示以下初始页面:
![]() |
[1] 和 [2] 中显示的值来自 [Form] 操作中字段的初始值。让我们来看几个例子:
private String textarea = "ligne1\nligne2\n";
textarea 字段的值解释了 [1A] 和 [2A] 的显示内容。
private String[] select3 = new String[]{"zéro", "deux"};
private String[] select3Values = new String[]{"zéro", "un", "deux", "trois", "quatre"};
select3Values 字段的值解释了 [1B] 中列表的内容。select3 字段的值解释了 [1B] 中选中的项目以及在 [2B] 中显示的内容。
如果我们在表单中输入其他值并提交,输入的值将被发布到 [Form] 操作的相应字段中。随后,[Form.jsp] 视图将重新显示。因此,我们处于与之前相同的情况,只是初始值已被发布的值所替换。以下是一个示例:


9.9. [Form1] 操作和 [Form1.jsp] 视图
配置文件 [example.xml] 配置了以下 [Form1] 操作:
<?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="Form" class="example.Form">
<result name="success">/example/Form.jsp</result>
</action>
<action name="Form1" class="example.Form1">
<result name="success">/example/Form1.jsp</result>
</action>
<action name="Form2" class="example.Form2">
<result name="success">/example/Form2.jsp</result>
</action>
</package>
</struts>
第 11–13 行:对 [Form1] 动作的调用将生成 [Form1.jsp] 视图。
[Form1] 动作如下:
package example;
import com.opensymphony.xwork2.ActionSupport;
public class Form1 extends ActionSupport{
// constructor without parameters
public Form1() {
}
// form fields
private Data data=new Data();
/**
* @return the data
*/
public Data getData() {
return data;
}
/**
* @param data the data to set
*/
public void setData(Data data) {
this.data = data;
}
}
[Form1] 的实现与 [Form] 完全相同,唯一的区别在于其模型部分已被移至一个独立的类——[Data] 类(第 11 行)。[Data] 类的定义如下:
package example;
public class Data {
// constructor without parameters
public Data() {
}
// form fields
private String textfield = "texte";
private String password = "secret";
private String textarea = "ligne1\nligne2\n";
private String select1 = "zéro";
private String[] select1Values = new String[]{"zéro", "un", "deux"};
private String select2 = "trois";
private String[] select2Values = new String[]{"zéro", "un", "deux", "trois", "quatre"};
private String[] select3 = new String[]{"zéro", "deux"};
private String[] select3Values = new String[]{"zéro", "un", "deux", "trois", "quatre"};
private String radio="bleu";
private String[] radioValues = new String[]{"bleu", "blanc", "rouge"};
private Boolean checkbox = false;
private String[] checkboxlist = new String[]{"vélo", "bus"};
private String[] checkboxlistValues = new String[]{"voiture", "tram", "vélo", "bus", "métro"};
private String hidden = "initial";
private String submitText;
// values selected in select3 field
public String getSelect3SelectedValues() {
return getValue(select3);
}
// values selected in checkboxlist field
public String getCheckboxlistSelectedValues() {
return getValue(checkboxlist);
}
// utilitarian method
public String getValue(String[] values) {
String result = "";
for (String value : values) {
result += " " + value;
}
return result;
}
// getters and setters
....
}
我们找到了之前在 [Form] 操作中声明的字段。
从逻辑上讲,[Form1.jsp] 视图与 [Form.jsp] 视图相似:
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title><s:text name="Form.titre"/></title>
<s:head/>
</head>
<body background="<s:url value="/ressources/standard.jpg"/>">
<h2><s:text name="Form.message"/></h2>
<h3><s:text name="Form.langues"/></h3>
<ul>
<li>
<s:url id="url" action="Form">
<s:param name="request_locale">en</s:param>
</s:url>
<s:a href="%{url}"><s:text name="Form.anglais"/></s:a>
</li>
<li>
<s:url id="url" action="Form">
<s:param name="request_locale">fr</s:param>
</s:url>
<s:a href="%{url}"><s:text name="Form.francais"/></s:a>
</li>
</ul>
<s:form name="formulaire">
<s:textfield name="data.textfield" key="Form.textfield" />
<s:password name="data.password" key="Form.password"/>
<s:textarea name="data.textarea" key="Form.textarea" cols="40" rows="5"/>
<s:select name="data.select1" list="data.select1Values" size="1" key="Form.select1" headerValue="<-- select 1 -->" headerKey="-1" />
<s:select name="data.select2" size="3" list="data.select2Values" key="Form.select2"/>
<s:select name="data.select3" size="3" list="data.select3Values" key="Form.select3" multiple="true"/>
<s:radio name="data.radio" list="data.radioValues" key="Form.radio"/>
<s:checkbox name="data.checkbox" key="Form.checkbox"/>
<s:checkboxlist name="data.checkboxlist" list="data.checkboxlistValues" key="Form.checkboxlist"/>
<s:hidden name="data.hidden" key="Form.hidden"/>
<s:submit key="Form.submitText" name="data.submitText"/>
</s:form>
<hr/>
<h2><s:text name="Confirmation.message"/></h2>
<table border="1">
<tr>
<th><s:text name="Confirmation.champ"/></th>
<th><s:text name="Confirmation.valeur"/></th>
</tr>
<tr>
<td><s:text name="Form.textfield"/></td>
<td><s:property value="data.textfield"/>
</tr>
<tr>
<td><s:text name="Form.password"/></td>
<td><s:property value="data.password"/>
</tr>
<tr>
<td><s:text name="Form.textarea"/></td>
<td><s:property value="data.textarea"/>
</tr>
<tr>
<td><s:text name="Form.select1"/></td>
<td><s:property value="data.select1"/>
</tr>
<tr>
<td><s:text name="Form.select2"/></td>
<td><s:property value="data.select2"/>
</tr>
<tr>
<td><s:text name="Form.select3"/></td>
<td><s:property value="data.select3SelectedValues"/>
</tr>
<tr>
<td><s:text name="Form.radio"/></td>
<td><s:property value="data.radio"/>
</tr>
<tr>
<td><s:text name="Form.checkbox"/></td>
<td><s:property value="data.checkbox"/>
</tr>
<tr>
<td><s:text name="Form.checkboxlist"/></td>
<td><s:property value="data.checkboxlistSelectedValues"/>
</tr>
<tr>
<td><s:text name="Form.hidden"/></td>
<td><s:property value="data.hidden"/>
</tr>
</table>
</body>
</html>
[Form] 和 [Form1] 视图之间的区别可通过下文第 27 行进行说明:
<s:textfield name="data.textfield" key="Form.textfield" />
该输入字段与 [Form1] 操作的 data.textfield 字段建立了读写绑定。请注意,执行完成后,视图可以访问 [Form1] 操作的属性。因此,前面的 name 属性将被解析为 [Form1].getData().getTextField()。因此,前面的两个 get 方法必须存在。
9.10. [Form2] 操作与 [Form2.jsp] 视图
配置文件 [example.xml] 配置了以下 [Form2] 操作:
<?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="Form" class="example.Form">
<result name="success">/example/Form.jsp</result>
</action>
<action name="Form1" class="example.Form1">
<result name="success">/example/Form1.jsp</result>
</action>
<action name="Form2" class="example.Form2">
<result name="success">/example/Form2.jsp</result>
</action>
</package>
</struts>
第 14–16 行:对 [Form2] 操作的调用将生成 [Form2.jsp] 视图。
[Form2] 动作如下:
package example;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
public class Form2 extends ActionSupport implements ModelDriven {
// constructor without parameters
public Form2() {
}
// action model
public Object getModel() {
return new Data();
}
}
- 第 6 行:[Form2] 操作实现了 [ModelDriven] 接口。只需实现一个方法:第 12 行中的 getModel 方法。该方法返回一个前面讨论过的 [Data] 类的实例。
[Form2] 操作实现 [ModelDriven] 接口这一事实带来了以下结果:在 [Form2] 操作之后显示的视图可以直接访问 [Form2] 操作模型的属性,该模型由 getModel() 方法返回。因此,[Form2.jsp] 视图恢复到了最初 [Form] 操作时的状态:
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title><s:text name="Form.titre"/></title>
<s:head/>
</head>
<body background="<s:url value="/ressources/standard.jpg"/>">
<h2><s:text name="Form.message"/></h2>
<h3><s:text name="Form.langues"/></h3>
<ul>
<li>
<s:url id="url" action="Form">
<s:param name="request_locale">en</s:param>
</s:url>
<s:a href="%{url}"><s:text name="Form.anglais"/></s:a>
</li>
<li>
<s:url id="url" action="Form">
<s:param name="request_locale">fr</s:param>
</s:url>
<s:a href="%{url}"><s:text name="Form.francais"/></s:a>
</li>
</ul>
<s:form name="formulaire">
<s:textfield name="textfield" key="Form.textfield" />
<s:password name="password" key="Form.password"/>
<s:textarea name="textarea" key="Form.textarea" cols="40" rows="5"/>
<s:select name="select1" list="select1Values" size="1" key="Form.select1" headerValue="<-- select 1 -->" headerKey="-1" />
<s:select name="select2" size="3" list="select2Values" key="Form.select2"/>
<s:select name="select3" size="3" list="select3Values" key="Form.select3" multiple="true"/>
<s:radio name="radio" list="radioValues" key="Form.radio"/>
<s:checkbox name="checkbox" key="Form.checkbox"/>
<s:checkboxlist name="checkboxlist" list="checkboxlistValues" key="Form.checkboxlist"/>
<s:hidden name="hidden" key="Form.hidden"/>
<s:submit key="Form.submitText" name="submitText"/>
</s:form>
<hr/>
<h2><s:text name="Confirmation.message"/></h2>
<table border="1">
<tr>
<th><s:text name="Confirmation.champ"/></th>
<th><s:text name="Confirmation.valeur"/></th>
</tr>
<tr>
<td><s:text name="Form.textfield"/></td>
<td><s:property value="textfield"/>
</tr>
<tr>
<td><s:text name="Form.password"/></td>
<td><s:property value="password"/>
</tr>
<tr>
<td><s:text name="Form.textarea"/></td>
<td><s:property value="textarea"/>
</tr>
<tr>
<td><s:text name="Form.select1"/></td>
<td><s:property value="select1"/>
</tr>
<tr>
<td><s:text name="Form.select2"/></td>
<td><s:property value="select2"/>
</tr>
<tr>
<td><s:text name="Form.select3"/></td>
<td><s:property value="select3SelectedValues"/>
</tr>
<tr>
<td><s:text name="Form.radio"/></td>
<td><s:property value="radio"/>
</tr>
<tr>
<td><s:text name="Form.checkbox"/></td>
<td><s:property value="checkbox"/>
</tr>
<tr>
<td><s:text name="Form.checkboxlist"/></td>
<td><s:property value="checkboxlistSelectedValues"/>
</tr>
<tr>
<td><s:text name="Form.hidden"/></td>
<td><s:property value="hidden"/>
</tr>
</table>
</body>
</html>
因此第27行,之前的内容是:
<s:textfield name="data.textfield" key="Form.textfield" />
现在改为:
<s:textfield name="textfield" key="Form.textfield" />
将模型置于视图之外的优势在于,这种架构更清晰地界定了MVC架构中每个元素的功能。M(模型)通过一个类被明确标识。此外,模型可能在操作(Action)之前就已存在。因此,如果一个Web应用程序管理一个人员数据库,那么Person类很可能已经存在。如果一个操作提供了一个用于创建新人员的输入表单,那么该操作的模型显而易见:它就是Person类。
我们已经看到,从 [Form] 操作切换到 [Form2] 操作是多么简单:
- 视图 V 的模型 M 已被封装在单独的类中
- 使用该模型生成视图的操作实现了带有 getModel 方法的 ModelDriven 接口,该方法返回模型 M 的实例。因此,该操作可视为 Struts 2 控制器 C(即 FilterDispatcher 类)的扩展。
读者可以将此方法应用于下文所述的任何操作。

