6. 表单 HTML
迄今为止,我们仅使用了一个包含两个输入字段的表单。在此,我们将尝试创建并处理一个使用常用图形组件(单选按钮、复选框、输入字段、下拉列表、列表)的表单。
6.1. 应用程序的视图
该应用程序只有两个视图。第一个视图显示一个空白表单:
QZXW2HTMLP000546ZQX 1 - formulaire

这个名为 formulaire.jsp 的第一个视图将让我们能够实现 struts-html 库中的各种标签。用户填写表单:

点击按钮 [Envoyer] 可确认已输入的值。这将是第二个视图:

通过这个第二视图,我们将能够使用另外两个标签库:struts-bean 和 struts-logic。链接 [Retour au formulaire] 允许我们返回已填写的表单。此时将回到第一个视图。
6.2. 应用程序架构
![]() |
- 表单(视图 1)将由一个名为 dynaFormulaire 的动态 Struts 对象表示,该对象的类型继承自 DynaActionForm。它将通过视图 formulaire.jsp 进行显示。
- Struts 动作 InitFormulaireAction 的目的是获取显示表单所需的数据
- 填写完成的表单将由 ForwardAction 操作处理,该操作仅需将请求重定向至第二个视图 confirmation.jsp。该视图将负责显示表单的值。
6.3. 应用程序的配置
6.3.1. 文件 server.xml
应用程序上下文将命名为 /formulaire2。因此,需在 Tomcat 的 server.xml 文件中添加以下行:
完成上述操作后,可能需要重启 Tomcat 以便其应用新的上下文。我们可以通过访问 URL http://localhost:8080/formulaire2 来验证该配置是否有效:

6.3.2. 文件 web.xml
该应用程序的配置文件 web.xml 将如下所示:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<taglib>
<taglib-uri>/WEB-INF/struts-html.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-html.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>/WEB-INF/struts-bean.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-bean.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>/WEB-INF/struts-logic.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-logic.tld</taglib-location>
</taglib>
</web-app>
针对之前提到的配置文件 web.xml,我们进行了一些修改:
- 引入了两个新的标签库:struts-bean 和 struts-logic。它们将在视图 confirmation.jsp 中使用。而视图 formulaire.jsp 则将使用 struts-html 库。
6.3.3. 文件 struts-config.xml
文件 struts-config.xml 内容如下:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
"http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">
<struts-config>
<form-beans>
<form-bean name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">
<form-property name="opt" type="java.lang.String" initial="non"/>
<form-property name="chk1" type="java.lang.String"/>
<form-property name="chk2" type="java.lang.String"/>
<form-property name="chk3" type="java.lang.String"/>
<form-property name="champSaisie" type="java.lang.String" initial=""/>
<form-property name="mdp" type="java.lang.String" initial=""/>
<form-property name="boiteSaisie" type="java.lang.String" initial=""/>
<form-property name="combo" type="java.lang.String"/>
<form-property name="listeSimple" type="java.lang.String"/>
<form-property name="listeMultiple" type="java.lang.String[]"/>
<form-property name="secret" type="java.lang.String" initial="xxx"/>
<form-property name="valeursCombo" type="java.lang.String[]" />
<form-property name="valeursListeSimple" type="java.lang.String[]" />
<form-property name="valeursListeMultiple" type="java.lang.String[]"/>
</form-bean>
</form-beans>
<action-mappings>
<action
path="/confirmation"
name="dynaFormulaire"
validate="false"
scope="session"
parameter="/vues/confirmation.jsp"
type="org.apache.struts.actions.ForwardAction"
/>
<action
path="/init"
name="dynaFormulaire"
validate="false"
scope="session"
type="istia.st.struts.formulaire.InitFormulaireAction"
>
<forward name="afficherFormulaire" path="/vues/formulaire.jsp"/>
</action>
<action
path="/affiche"
parameter="/vues/formulaire.jsp"
type="org.apache.struts.actions.ForwardAction"
/>
</action-mappings>
<message-resources
parameter="ApplicationResources"
null="false"/>
</struts-config>
其中包含三个主要部分:
- <form-beans> 部分中的表单声明
- <action-mappings> 部分中的操作声明
- 资源文件的声明位于 <message-resources> 部分
6.3.4. 应用程序的表单对象(Beans)
用于表示应用程序中 HTML 表单的对象,是 ActionForm 类型或其派生类型(如 DynaActionForm、DynaValidatorForm 等)的对象。 之所以称其为 Bean,是因为它们的构造遵循 JavaBeans 的规则。我们的应用程序中仅有一个表单 Bean,名为 dynaFormulaire,其类型是从 DynaActionForm 派生而来的。它将在以下情况下使用:
- 包含显示视图 1 所需的数据
- 在用户提交(submit)表单时,从视图 1 中获取表单值
- 包含显示视图 2 所需的数据
Bean dynaFormulaire 的结构与视图 1 的表单密切相关。让我们来分析一下该表单:
![]() |
编号 | 类型 HTML | 角色 |
<input name="opt" type="radio" value="是"> <input name="opt" type="radio" value="否"> | 相互关联的单选按钮组(名称相同) | |
<input name="chk1" type="radio" value="on"> <input name="chk2" type="radio" value="on"> <input name="chk3" type="radio" value="on"> | 一组复选框 (名称不同) | |
<input type="text" name="champSaisie"> | 一个输入字段 | |
<input type="password" name="mdp"> | 一个密码输入框 | |
<textarea name="boiteSaisie">...</textarea> | 多行输入框 | |
<select name="combo" size="1">..</select> | 一个下拉列表 | |
<select name="listeSimple" size="3">..</select> | 单选列表 | |
<select name="listeMultiple" size="3" multiple>..</select> | 多选列表 | |
<input type="button" value="清除" onclick='effacerListe("listeSimple")'> | 用于取消选中 listeSimple (7) 中选中的项目 | |
<input type="button" value="清除" onclick='effacerListe("listeMultiple")'> | 用于取消选中 listeMultiple(8)中选中的项目 | |
<input type="submit" value="提交"> | 表单提交按钮 | |
<input type="hidden" name="secret" value="..."> | 一个隐藏字段 |
我们来区分几种情况:
- 对象 dynaFormulaire 用于存储上述表单 HTML 的值,该表单将通过按钮 [Envoyer] 提交。 因此,它必须包含与表单 HTML 相同的字段。字段类型由以下规则确定:
- 如果字段 HTML 仅提供一个值,则 dynaFormulaire 的字段类型为 java.lang.String
- 如果字段 HTML 提供多个值,则 dynaFormulaire 字段的类型为 java.lang.String[]
在上述表单 HTML 中,只有字段 listeMultiple 可能与多个值相关联(即用户所选的值)。因此,对象 dynaFormulaire 的初始定义如下:
<form-bean name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">
<form-property name="opt" type="java.lang.String" initial="non"/>
<form-property name="chk1" type="java.lang.String"/>
<form-property name="chk2" type="java.lang.String"/>
<form-property name="chk3" type="java.lang.String"/>
<form-property name="champSaisie" type="java.lang.String" initial=""/>
<form-property name="mdp" type="java.lang.String" initial=""/>
<form-property name="boiteSaisie" type="java.lang.String" initial=""/>
<form-property name="combo" type="java.lang.String"/>
<form-property name="listeSimple" type="java.lang.String"/>
<form-property name="listeMultiple" type="java.lang.String[]"/>
<form-property name="secret" type="java.lang.String" initial="xxx"/>
</form-bean>
如何将Web客户端发送的HTML表单中的值填入dynaFormulaire表单中?
如果选中了字段 HTML <input type="radio" name="opt" value="是">,则字段 opt 将接收值“是”;如果选中了字段 <input type="radio" name="opt" value="否">,则接收值“否”。 | |
如果选中了字段 HTML <input name="chk1" type="radio" value="1">,则字段 chk1 将获得值“on”;否则为空。在后一种情况下,字段 chk1 将保留其先前值。 | |
同上 | |
同上 | |
字段 champSaisie 将接收用户在字段 HTML <input type="text" name="champSaisie"> 中输入的文本。该文本可以是空字符串。 | |
mdp 字段将接收用户在 HTML <input type="password" name="mdp"> 字段中输入的文本。该文本可以是空字符串。 | |
字段 boiteSaisie 将接收用户在字段 HTML 中输入的文本 <textarea name="boiteSaisie">...</textarea>。 该文本构成一个单一字符串,由用户输入的各行组成,行与行之间以字符序列“\r\n”分隔。生成的文本可能为空字符串。 | |
下拉列表框将接收用户在字段 HTML <select name="combo" size="1">..</select> 中选定的选项。所选选项即为下拉列表中显示的选项。 如果选中的 HTML 选项属于 <option value="XX">YY</option> 类型,则下拉列表字段将接收值 "XX"。 如果所选的 HTML 选项属于 <option>YY</option> 类型,则下拉列表字段将接收值“YY”。 | |
listeSimple 字段将接收用户在 HTML 字段中选定的选项 <select name="listeSimple" size="..">..</select>(如果存在选项)。 如果没有选项,字段 listeSimple 将不会接收任何值,并保留其先前值。实际分配给字段 listeSimple 的值遵循下拉列表中指定的规则。 | |
类型为 String[] 的字段 listeMultiple 将接收用户在字段 HTML <select name="listeMultiple" size=".." multiple>..</select> 中选定的选项(如有)。 若无选项,数组 listeMultiple 将不接收任何值,其内容保持不变。实际赋予数组 listeMultiple 的值遵循下拉列表中指定的规则。 | |
隐藏字段将接收来自字段 HTML 的值 XX <input type="hidden" name="secret" value="XX">。该文本可能为空字符串。 |
- 对象 dynaFormulaire 用于为视图 1 提供初始内容。前几个字段的值将用于以下目的:
必须设置为“是”或“否”,以便浏览器知道应选中哪个单选按钮 | |
如果 chk1 的值为“on”,则复选框将被选中,否则不会被选中 | |
同上 | |
同上 | |
字段值将显示在输入框中champSaisie | |
该字段的值将显示在密码输入框中 | |
该字段的值将显示在输入框中 boiteSaisie | |
该字段的值指定在表单显示时应选中的下拉列表项 | |
同上 | |
数组 listeMultiple 中的值指定在显示表单时应选中的多选列表中的哪些选项 | |
该字段的值将被赋给 HTML 字段的 value 属性。 |
视图 1 还需要其他信息:
- 下拉列表中要显示的值列表
- 要在下拉列表中显示的值列表 listeSimple
- 要在列表 listeMultiple 中显示的值列表
向视图提供这些信息有多种方法。例如,将数组放入传递给视图的查询中即可。在此,我们将这些数组放入 Bean dynaFormulaire 中:
<form-bean name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">
...
<form-property name="valeursCombo" type="java.lang.String[]" />
<form-property name="valeursListeSimple" type="java.lang.String[]" />
<form-property name="valeursListeMultiple" type="java.lang.String[]"/>
</form-bean>
表单 dynaFormulaire 将由 /init 操作初始化,该操作将调用一个名为 InitFormulaireAction 的 Action 派生对象。 该对象将负责创建显示三个列表所需的三个数组,并将其放入 dynaFormulaire Bean 中。 配置文件将该 Bean 的作用域设为会话。这意味着 Struts 控制器会将会话中的该 Bean。因此,在两次请求-响应循环之间,我们无需重新生成它。此外,/init 操作也只会被调用一次。
- 对象 dynaFormulaire 同样用于提供视图 2 的内容。该视图仅负责显示这些值。
6.3.5. 应用程序的动作
操作由 Action 类型或其派生类型的对象负责。操作的配置在 <action-mappings> 标签内进行:
<action-mappings>
<action
path="/confirmation"
name="dynaFormulaire"
validate="false"
scope="session"
parameter="/vues/confirmation.jsp"
type="org.apache.struts.actions.ForwardAction"
/>
<action
path="/init"
name="dynaFormulaire"
validate="false"
scope="session"
type="istia.st.struts.formulaire.InitFormulaireAction"
>
<forward name="afficherFormulaire" path="/vues/formulaire.jsp"/>
</action>
<action
path="/affiche"
parameter="/vues/formulaire.jsp"
type="org.apache.struts.actions.ForwardAction"
/>
</action-mappings>
需要注意的是,并非所有操作都关联着表单。上文中的 /affiche 操作便是如此。在详细介绍每个操作之前,让我们回顾一下 <action> 标签内操作与表单这对组合的工作原理:
- 一个操作始于 Web 客户端的请求,终于响应页面的发送。这就是 Web 客户端与服务器之间的请求-响应循环。请求由类型为 ActionServlet 或其派生的 Struts 控制器接收。该控制器也会发送响应。
- 如果尚未存在,则会创建类型为 ActionForm 或其派生类的表单 Bean。控制器会检查是否能在 scope 指定的作用域中找到名为 name 的对象。如果存在,则直接使用该对象;如果不存在,则创建该对象并将其放入 scope 指定的作用域中,同时将其与 name 指定的属性关联起来。
例如在 /init 操作中,控制器将执行 request.getSession().getAttribute("dynaFormulaire") 来判断 dynaFormulaire 是否已被创建。 如果尚未创建,它将创建该对象,并通过类似 request.getSession() 的语句将其放入会话中。setAttribute("dynaFormulaire",new DynaFormulaire(...))。
- 控制器还将查找类型属性指定的Action对象。若未找到,则创建该对象;否则直接使用现有对象。
- 表单 Bean 的 reset 方法将被调用。该 Bean 除初始创建时外,均会被回收利用。因此其中可能包含需要“清理”的数据。这将在 ActionForm Bean 或其派生类的 reset 方法中进行。
- 如果该操作是表单提交的目标,则客户端请求中包含的表单值会被复制到表单Bean中同名的字段中。需要注意的是,在此复制操作之前已调用了reset方法。
- 如果配置中指定了 validate="true" 属性,则会调用表单 Bean 的 validate 方法。该方法必须对 Bean 中的数据进行验证。 这种验证通常仅在表单刚通过 POST 请求接收新数据,且需要验证这些数据有效性时进行。该方法会将可能的错误列表以 ActionErrors 对象的形式返回给控制器。
- 如果 ActionErrors 对象不为空,控制器将显示由操作的 input 属性指定的视图。
- 如果未要求进行数据验证,或者验证成功,控制器将执行与当前操作关联的 Action 类型对象或其派生对象的 execute 方法。Web 客户端的请求将在该方法中得到处理。execute 方法返回一个由字符串类型键索引的 ActionForward 对象。 这些键由已配置操作的 forward 标签声明。在本例中,操作 /init 仅有一个 forward 标签。它将键 "afficherFormulaire" 与视图 formulaire.jsp 关联。
- 控制器会显示与接收到的键关联的视图。该视图实际上可能是一个操作,在这种情况下,上述过程将重复进行。
/init 操作
<action
path="/init"
name="dynaFormulaire"
validate="false"
scope="session"
type="istia.st.struts.formulaire.InitFormulaireAction"
>
<forward name="afficherFormulaire" path="/vues/formulaire.jsp"/>
</action>
- /init 操作通常在第一次请求-响应循环中发生一次,即当用户请求 URL 时http://localhost:8080/formulaire2/init.do
- 对象 dynaFormulaire 被创建或回收。根据 scope 属性的要求,该对象会被取回(回收)或放入(创建)会话中。
- 其 reset 方法被调用。该方法应执行什么操作?通常情况下,我们会将 ActionForm 对象的字段重置为默认值。但在此处,我们不会这样做,因为 dynaFormulaire 对象被放置在会话中(scope="session")。 因此,dynaFormulaire 的字段必须保留其值。在初始创建 dynaFormulaire 对象时,这些值是什么?有两种情况:
- 字段在配置文件中指定了初始值:
在此情况下,Struts 控制器将使用该初始值创建该字段。
- 该字段在配置中未指定初始值:此时将应用 Java 的初始化规则。通常,数值型字段的初始值为零,字符串型字段的初始值为空字符串,其他对象的初始值为 null。
让我们来看一下 dynaFormulaire 的初始配置:
<form-bean name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">
<form-property name="opt" type="java.lang.String" initial="non"/>
<form-property name="chk1" type="java.lang.String"/>
<form-property name="chk2" type="java.lang.String"/>
<form-property name="chk3" type="java.lang.String"/>
<form-property name="champSaisie" type="java.lang.String" initial=""/>
<form-property name="mdp" type="java.lang.String" initial=""/>
<form-property name="boiteSaisie" type="java.lang.String" initial=""/>
<form-property name="combo" type="java.lang.String"/>
<form-property name="listeSimple" type="java.lang.String"/>
<form-property name="listeMultiple" type="java.lang.String[]"/>
<form-property name="secret" type="java.lang.String" initial="xxx"/>
<form-property name="valeursCombo" type="java.lang.String[]" />
<form-property name="valeursListeSimple" type="java.lang.String[]" />
<form-property name="valeursListeMultiple" type="java.lang.String[]"/>
</form-bean>
dynaFormulaire 字段在创建后的初始值如下:
字段 | 初始值 |
"否" | |
空字符串 | |
空字符串 | |
空字符串 | |
空字符串 | |
空字符串 | |
空字符串 | |
空字符串数组 | |
"xxx" | |
空字符串数组 |
- 可以设想,dynaFormulaire 的 reset 方法会为三个数组赋值,这些数组将用于填充视图 formulaire.jsp 中的三个列表。这里之所以可行,是因为这三个数组的数据是任意生成的。 然而,最常见的情况是这些数据来自应用程序的模型,即 MVC 中的 M。 在此,为了不使示例过于复杂,我们将采取折中方案,让 InitFormulaireAction 操作(即 MVC 中的 C 部分)生成这些值。
- dynaFormulaire 无需编写 reset 方法,因为其派生类 ActionForm 已包含该方法,且该方法不执行任何操作(不进行初始化)。
- 一旦调用 dynaFormulaire 的 reset 方法,控制器会检查该操作的 validate 属性。此时,该属性值为 "false"。因此不会调用 dynaFormulaire 的 validate 方法。
- InitFormulaireAction 对象被创建(或若已存在则被回收),并调用其 execute 方法。 正是该方法会将任意值赋给 dynaFormulaire 的三个数组:valeursCombo、valeursListeSimple 和 valeursListeMultiple。 该方法返回一个键为“afficherFormulaire”的 ActionForward。
- 控制器显示视图 /vues/formulaire.jsp,该视图通过 /init 操作的 forward 标签与键 "afficherFormulaire" 相关联。
/confirmation 操作
<action
path="/confirmation"
name="dynaFormulaire"
validate="false"
scope="session"
parameter="/vues/confirmation.jsp"
type="org.apache.struts.actions.ForwardAction"
/>
- 当用户点击视图1中的按钮[Envoyer]时,将触发/confirmation操作。此时,浏览器会将用户填写好的表单“提交”给Struts控制器。
- dynaFormulaire 对象被纳入会话
- 其 reset 方法被调用。该方法调用后,Struts 控制器将把客户端提交的表单字段值复制到 dynaFormulaire 中同名的字段中。让我们回顾一下后者的字段列表,看看复制过程是如何进行的:
字段 | 关联的 HTML 代码 | 字段值 (复制表单值后) |
<input type="radio" name="opt" value="是">是 <input type="radio" name="opt" value="no" checked="checked">否 | - 根据选中的单选按钮,返回“是”或“否” | |
<input type="checkbox" name="chk1" value="on"> | - 若已勾选 chk1 复选框,则返回 "on" - 如果未勾选chk1复选框,则保留其先前值 | |
<input type="checkbox" name="chk2" value="on"> | - 若已勾选 chk2 复选框,则值为 "on" - 如果未勾选 chk2 复选框,则保留其先前值 | |
<input type="checkbox" name="chk2" value="on"> | - 若已勾选 chk3 复选框,则为 "on" - 如果未勾选 chk3 复选框,则保持其先前值 | |
<input type="text" name="champSaisie" value=""> | - 用户在 champSaisie 中输入的值 | |
<input type="password" name="mdp" value=""> | - 用户在 mdp 中输入的值 | |
<textarea name="boiteSaisie"></textarea> | - 用户在 boiteSaisie 中输入的值 | |
<select name="combo">...</select> | - 用户在 combo 中选择的值 | |
<select name="listeSimple" size="3">...</select> | - 用户在 listeSimple 中选择的值 | |
<select name="listeMultiple" multiple="multiple" size="5"> | - 包含用户在 listeMultiple 中所选值的字符串数组 | |
<input type="hidden" name="secret" value="xxx"> | - "xxx"。 |
对于那些在浏览器发送的请求中未必会收到值的字段,我们遇到了一些困难。例如复选框 chk1 至 chk3,以及两个列表 listeSimple 和 listeMultiple。 在这种情况下,这些字段会保留其先前值,即在上一个请求-响应循环中获取的值。
以复选框 chk1 为例,假设在上一轮请求-响应循环中,用户已勾选该复选框。此时浏览器会在请求的参数串中发送 chk1="on" 这一信息。因此,构建器将值 "on" 赋给了 dynaFormulaire 中的 chk1 字段。 现在假设在当前循环中,用户未勾选 chk1 复选框。在这种情况下,浏览器在新请求的参数串中不会发送类似 chk1="off" 的内容,而是什么也不发送。 因此,dynaFormulaire中的chk1字段将保留其"on"值,从而导致该值无法反映用户提交表单时的实际状态。我们将使用dynaFormulaire的reset方法来解决此问题。 我们将在此方法中将 chk1、chk2、chk3 这三个字段设置为“off”。以 chk1 为例,假设用户:
- 勾选了 chk1 复选框。此时浏览器会发送 chk1="on" 的信息,dynaFormulaire 中的 chk1 字段将变为 "on"
- 未勾选 chk1 复选框。此时浏览器不会发送 chk1 字段的值,该字段将保持其之前的“off”值。在两种情况下,dynaFormulaire 中 chk1 字段记录的值都是正确的。
对于列表 listeSimple 和 listeMultiple,问题也是类似的。如果在这两个列表中未选择任何选项,它们将不会出现在查询参数中,因此会保留其之前的值。 在 dynaFormulaire 的 reset 方法中,我们将使用空字符串重置 listeSimple,并使用长度为 0 的字符串数组重置 listeMultiple。
- 一旦调用 dynaFormulaire 的 reset 方法,控制器就会将客户端请求中发送给它的信息复制回 dynaFormulaire 的字段中
- 创建或回收一个 ForwardAction 对象,并调用其 execute 方法。 ForwardAction 是一个预定义类,它返回一个 ActionForward 对象,该对象指向由操作的 "parameter" 属性定义的视图,此处为 /vues/confirmation.jsp。
- 控制器发送该视图。循环结束。
该操作 /显示
<action
path="/affiche"
parameter="/vues/formulaire.jsp"
type="org.apache.struts.actions.ForwardAction"
/>
- /affiche 操作由视图 2 中的链接 [Retour vers le formulaire] 触发。
- 此处没有与该操作关联的表单。因此,系统会立即执行 ForwardAction 对象的 execute 方法,该方法将返回一个指向视图 /vues/formulaire.jsp 的 ActionForward 对象。
6.3.6. 应用程序的消息文件
文件 struts-config.xml 的第三部分是消息文件:
文件 ApplicationResources.properties 位于 WEB-INF/classes 目录下。该文件为空。 即使为空,也必须在配置文件中声明,否则稍后将要介绍的 struts-bean 标签库会报错。该库被视图 confirmation.jsp 使用。
6.4. 视图代码
6.4.1. 视图 formulaire.jsp
需要提醒的是,该视图在以下两种情况下显示:
- 在首次请求-响应循环中调用 /init 动作时
- 在后续请求-响应循环中调用 /affiche 操作时
视图 formulaire.jsp 的代码如下:
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<html>
<head>
<title>formulaire</title>
</head>
<body background='<html:rewrite page="/images/standard.jpg"/>'>
<h3>Formulaire Struts</h3>
<hr>
<html:form action="/confirmation" name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">
<table border="0">
<tr>
<td>bouton radio</td>
<td>
<html:radio name="dynaFormulaire" property="opt" value="oui">Oui</html:radio>
<html:radio name="dynaFormulaire" property="opt" value="non">Non</html:radio>
</td>
</tr>
<tr>
<td>Cases à cocher</td>
<td>
<html:checkbox name="dynaFormulaire" property="chk1">1</html:checkbox>
<html:checkbox name="dynaFormulaire" property="chk2">2</html:checkbox>
<html:checkbox name="dynaFormulaire" property="chk3">3</html:checkbox>
</td>
</tr>
<tr>
<td>Champ de saisie</td>
<td>
<html:text name="dynaFormulaire" property="champSaisie" />
</td>
</tr>
<tr>
<td>Mot de passe</td>
<td>
<html:password name="dynaFormulaire" property="mdp" />
</td>
</tr>
<tr>
<td>Boîte de saisie multilignes</td>
<td>
<html:textarea name="dynaFormulaire" property="boiteSaisie" />
</td>
</tr>
<tr>
<td>Combo</td>
<td>
<html:select name="dynaFormulaire" property="combo">
<html:options name="dynaFormulaire" property="valeursCombo"/>
</html:select>
</td>
</tr>
<tr>
<td>
<table>
<tr>
<td>Liste à sélection unique</td>
</tr>
<tr>
<td>
<input type="button" value="Effacer" onclick="this.form.listeSimple.selectedIndex=-1"/>
</td>
</tr>
</table>
<td>
<html:select name="dynaFormulaire" property="listeSimple" size="3">
<html:options name="dynaFormulaire" property="valeursListeSimple"/>
</html:select>
</td>
</tr>
<tr>
<td>
<table>
<tr>
<td>Liste à sélection multiple</td>
</tr>
<tr>
<td>
<input type="button" value="Effacer" onclick="this.form.listeMultiple.selectedIndex=-1"/>
</td>
</tr>
</table>
</td>
<td>
<html:select name="dynaFormulaire" property="listeMultiple" size="5" multiple="true">
<html:options name="dynaFormulaire" property="valeursListeMultiple"/>
</html:select>
</td>
</tr>
</table>
<html:hidden name="dynaFormulaire" property="secret"/>
<br>
<hr>
<html:submit>Envoyer</html:submit>
</html:form>
</body>
</html>
此页面 JSP 使用了来自 struts-html 标签库的标签。请注意,要使用标签库,必须:
- 在应用程序的 web.xml 文件中使用 <tag-lib> 标签进行声明
<taglib>
<taglib-uri>/WEB-INF/struts-html.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-html.tld</taglib-location>
</taglib>
- 将该库的代码放置在应用程序目录树的某个位置,此处为 WEB-INF/struts-html.tld
- 在使用该库的页面开头声明其引用:
视图 formulaire.jsp 使用了以下标签,现对其进行说明:
<body background="<html:rewrite page="/images/standard.jpg"/>"> | |||
html:rewrite 标签允许在 URL 中省略应用程序名称。它有一个属性:
如上所述,如果决定将应用程序命名为formulaire3,则background属性的代码无需重写。html:rewrite标签将生成新的代码HTML background="/formulaire3/images/standard.jpg" |
<html:form action="/confirmation" name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire"> | |||||||
html:form 标签用于生成 HTML 表单标签。它具有多个属性:
可以看到,默认情况下,生成的代码 HTML 使用了方法 POST。 在同一代码 HTML 中,操作的 URL 已被重写,使其前缀为应用程序名称,后缀为 .do。 |
<html:radio name="dynaFormulaire" property="opt" value="是">是</html:radio> | |||||||
html:radio 标签用于生成 HTML <input type="radio" ...> 标签。它支持多种属性:
开始标签和结束标签之间的文本将显示在单选按钮旁边。 |
<html:checkbox name="dynaFormulaire" property="chk1">1</html:checkbox> | |||||||
html:checkbox 标签用于生成 HTML <input type="checkbox" ...> 标签。它支持多种属性:
开始标签和结束标签之间的文本即为复选框旁显示的文本。 |
<html:text name="dynaFormulaire" property="champSaisie" /> | |||||||
html:text 标签用于生成 HTML <input type="text" ...> 标签。它支持多种属性:
|
<html:password name="dynaFormulaire" property="mdp" /> | |
html:password 标签用于生成 HTML <input type="password" ...> 标签。它支持多种属性: |
<html:textarea name="dynaFormulaire" property="boiteSaisie" /> | |||||||
html:textarea 标签用于生成 HTML <textarea>...</textarea> 标签。它支持多种属性:
|
<html:select name="dynaFormulaire" property="combo">....</html:select> | |||||||
html:select 标签用于生成 HTML <select>...</select> 标签。它支持多种属性:
|
<html:select name="dynaFormulaire" property="combo"> <html:options name="dynaFormulaire" property="valeursCombo"/> </html:select> | |||||
html:options 标签用于在 HTML <select> 标签内生成 HTML <option>...</option> 标签。指定如何查找下拉列表填充值的方法有多种。在此,我们使用了 name 和 property 属性:
|
其余两个列表的生成方式与前一个类似:
<html:select name="dynaFormulaire" property="listeSimple" size="3">
<html:options name="dynaFormulaire" property="valeursListeSimple"/>
</html:select>
上文中,我们指定了与 1 不同的 size 属性,以生成列表而非下拉框。
<html:select name="dynaFormulaire" property="listeMultiple" size="5" multiple="true">
<html:options name="dynaFormulaire" property="valeursListeMultiple"/>
</html:select>
上文中,我们设置了 multiple="true" 属性,以实现多选列表。
<html:hidden name="dynaFormulaire" property="secret"/> | |||||
html:hidden 标签用于生成 HTML <input type="hidden" ...> 标签。
|
要充分理解视图 formulaire.jsp 与其在内存中表示该视图的 Bean dynaFormulaire 之间的关联,需注意 Bean dynaFormulaire 同时用于读写操作:
![]() |
当用户点击表单中的 [Envoyer] 按钮时,会触发该请求。此时,浏览器将表单 HTML “提交”至 /confirmation 操作。 我们之前已经解释过此时发生的情况,特别是 dynaFormulaire 表单中的字段将接收来自 HTML 表单中同名字段的值。
当控制器响应请求时,要求显示视图 formulaire.jsp,此时会发生什么?让我们逐一重新审视这些标签:
<body background="<html:rewrite page="/images/standard.jpg"/>"> | |
生成代码 HTML |
<html:form action="/confirmation" name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire"> ... </html:form> | |
生成代码 HTML |
<html:radio name="dynaFormulaire" property="opt" value="是">是</html:radio> <html:radio name="dynaFormulaire" property="opt" value="no">否</html:radio> | |
如果 dynaFormulaire 的 opt 字段值为 "是",则生成代码 HTML |
<html:checkbox name="dynaFormulaire" property="chk1">1</html:checkbox> <html:checkbox name="dynaFormulaire" property="chk2">2</html:checkbox> <html:checkbox name="dynaFormulaire" property="chk3">3</html:checkbox> | |
如果 dynaFormulaire 的 chk1 和 chk3 字段为“on”,且 chk2 字段为“off”,则生成代码 HTML |
<html:text name="dynaFormulaire" property="champSaisie" /> | |
如果字段 champSaisie 的值为“这是测试”,则生成代码 HTML |
<html:password name="dynaFormulaire" property="mdp" /> | |
如果 mdp 字段值为 "azerty",则生成代码 HTML |
<html:password name="dynaFormulaire" property="mdp" /> | |
如果 mdp 字段值为 "azerty",则生成代码 HTML |
<html:password name="dynaFormulaire" property="mdp" /> | |
如果 mdp 字段值为 "azerty",则生成代码 HTML |
<html:select name="dynaFormulaire" property="combo"> <html:options name="dynaFormulaire" property="valeursCombo"/> </html:select> | |
如果下拉框的值为“combo2”,则生成代码 HTML |
<html:select name="dynaFormulaire" property="listeSimple" size="3"> <html:options name="dynaFormulaire" property="valeursListeSimple"/> </html:select> | |
如果字段 listeSimple 的值为 "simple1",则生成代码 HTML |
<html:select name="dynaFormulaire" property="listeMultiple" size="5" multiple="true"> <html:options name="dynaFormulaire" property="valeursListeMultiple"/> </html:select> | |
如果字段 listeMultiple 是数组 {"multiple0","multiple2"},则生成代码 HTML |
<html:hidden name="dynaFormulaire" property="secret"/> | |
如果“secret”字段的值为“xxx”,则生成代码 HTML |
<html:submit>提交</html:submit> | |
生成代码 HTML |
最后需要说明的是页面 JSP 中包含的 JavaScript 代码,该代码与两个按钮 [Effacer] 相关联,用于取消在列表 listeSimple 和 listeMultiple 中选中的项目:
<input type="button" value="Effacer" onclick="this.form.listeSimple.selectedIndex=-1"/>
<input type="button" value="Effacer" onclick="this.form.listeMultiple.selectedIndex=-1"/>
标签
<html:form action="/confirmation" name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">
生成了以下代码 HTML:
为了理解与按钮相关的 JavaScript 代码 [Effacer],让我们回顾一下在使用该网页文档的 JavaScript 代码中,网页文档的各个元素是如何被命名的:
数据 | 含义 |
指代整个网页文档 | |
指代文档中定义的表单集合 | |
表示文档中的第 i 个表单 | |
表示表单 <form>,其 name 属性等于 "nomFormulaire" | |
指表单 <form>,其 name 属性等于 "nomFormulaire" | |
指由表达式 [formulaire] 指定的表单的成员元素集合。该集合包含指定表单中的所有 <input>、<textarea>、<select> 标签。 | |
表示 [formulaire] 集合中的第 i 个元素 | |
指[formulaire]中name属性值为nomComposant的元素 | |
指代 [formulaire] 中 name 属性等于 nomComposant 的元素 | |
表示表单 [formulaire] 中组件 [composant] 的值,当该表单的代码 HTML 可能具有 value 属性时(<input>,<textarea>) | |
表示在列表中选中选项的索引。可用于读写操作。将此属性设置为 -1 将取消选中列表中的所有项目。 | |
表示与 <select> 标签关联的选项数组 | |
表示指定 <select> 标签中的第 i 个选项 | |
布尔值,表示所指标签 [select] 的第 i 个选项是否被选中(true)。可用于读写操作 |
让我们回顾一下这两个按钮的 JavaScript 代码:
<input type="button" value="Effacer" onclick="this.form.listeSimple.selectedIndex=-1"/>
<input type="button" value="Effacer" onclick="this.form.listeMultiple.selectedIndex=-1"/>
在按钮点击事件发生时,与“onclick”属性关联的代码将被执行。此处为内联代码。通常情况下,我们会编写 onclick="function(...)",其中 function 是在 <script language="javascript">...</script> 标签内定义的函数。上述代码的作用是什么?让我们对第一个按钮的代码进行注释:
指代包含该按钮的网页文档 | |
指代包含该按钮的表单 | |
表示表单中的组件 listeSimple | |
表示在 listeSimple 中所选选项的索引。将此属性设置为 -1 将导致所有选项均被取消选中。 |
6.4.2. 视图 confirmation.jsp
需要说明的是,该视图是在 /confirmation 操作(c.a.d)执行后显示的,此时 formulaire.jsp 视图中的表单已由 Web 客户端提交。其唯一目的是显示用户输入的值。其代码如下:
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
<html>
<head>
<title>Confirmation</title>
</head>
<body background="<html:rewrite page="/images/standard.jpg"/>">
<h3>Confirmation des valeurs saisies</h3>
<hr/>
<table border="1">
<tr>
<td>Bouton radio</td>
<td><bean:write name="dynaFormulaire" scope="session" property="opt"/></td>
</tr>
<tr>
<td>Case à cocher chk1</td>
<td><bean:write name="dynaFormulaire" scope="session" property="chk1"/></td>
</tr>
<tr>
<td>Case à cocher chk2</td>
<td><bean:write name="dynaFormulaire" scope="session" property="chk2"/></td>
</tr>
<tr>
<td>Case à cocher chk3</td>
<td><bean:write name="dynaFormulaire" scope="session" property="chk3"/></td>
</tr>
<tr>
<td>Champ de saisie</td>
<td><bean:write name="dynaFormulaire" scope="session" property="champSaisie"/></td>
</tr>
<tr>
<td>Mot de passe</td>
<td><bean:write name="dynaFormulaire" scope="session" property="mdp"/></td>
</tr>
<tr>
<td>Boîte de saisie</td>
<td><bean:write name="dynaFormulaire" scope="session" property="boiteSaisie"/></td>
</tr>
<tr>
<td>combo</td>
<td><bean:write name="dynaFormulaire" scope="session" property="combo"/></td>
</tr>
<tr>
<td>liste simple</td>
<td><bean:write name="dynaFormulaire" scope="session" property="listeSimple"/></td>
</tr>
<logic:iterate id="choix" indexId="index" name="dynaFormulaire" property="listeMultiple">
<tr>
<td>liste multiple[<bean:write name="index"/>]</td>
<td><bean:write name="choix"/></td>
</tr>
</logic:iterate>
</table>
<br>
<html:link page="/affiche.do">
Retour au formulaire
</html:link>
</body>
</html>
在此我们引入两个新的标签库:struts-bean 和 struts-logic。struts-bean 库允许访问位于请求、会话或应用程序上下文中的对象。 struts-logic 库则允许通过标签引入执行逻辑。这两个库绝非必不可少。正如我们所见,一个 JSP 页面可以:
- 从请求(request.getAttribute(...))、会话(session.getAttribute(...))或应用程序上下文中获取对象
- 通过 <%= 变量 %> 变量在代码 HTML 中包含动态部分
- 包含 Java 代码 <% Java 代码 %>
在 JSP 页面中包含 Java 代码,这让所有希望严格区分应用逻辑(Java 代码)与呈现(使用标签)的人感到困扰。因此,我们为他们开发了标签库。
我们将参照 formulaire.jsp 视图的做法,对 confirmation.jsp 代码中出现的每个标签进行说明(如果这些标签在 formulaire.jsp 视图中尚未出现)。 首先,请注意该页面开头声明了将要使用的三个标签库:
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
此外,请注意这三个库必须在应用程序的 web.xml 文件中进行声明。现在,我们对 formulaire.jsp 文档中的标签进行注释:
在当前的 HTML 流中写入一个值。bean:write 标签支持以下属性: name:要使用的对象名称 scope:搜索该对象的作用域(request、session、context) property:指定要写入属性的 name 所指对象的字段。该字段可以是任何类型的对象。将使用该对象的 toString 方法。 此处将写入 dynaFormulaire 对象中 opt 字段的值。如果用户勾选了 value="是" 的单选按钮,则返回“是”;如果勾选了 value="否" 的单选按钮,则返回“否” |
写入 dynaFormulaire 中 chk1 字段的值。如果用户勾选了复选框,则返回“on”;否则返回“off”。chk2、chk3 同理。 |
写入 champSaisie 字段的值,即用户在该字段中输入的文本。mdp(boiteSaisie)亦同。 |
写入下拉列表字段 dynaFormulaire 的值。这将获取用户所选 <option> 元素的 value 属性。 |
写入字段 listeSimple 的值,该值来自 dynaFormulaire。如果用户选择了 <option> 元素,则该字段将包含该元素的 value 属性值;否则,该字段将包含空字符串。 |
在此,我们引入逻辑标签。我们处理的是一个多选列表。对象 dynaFormulaire 的字段 listeMultiple 的值是一个字符串数组。在 Java 中,我们会编写一个循环。 logic:iterate 标签使我们无需编写 Java 代码即可实现相同的循环。在该示例中,logic:iterate 标签具有以下属性: name="dynaFormulaire":要使用的对象名称 property="listeMultiple":指明 name 属性所指向的对象中,包含将在循环中遍历的集合的属性名称。此处该集合即为 listeMultiple 中选定值的数组。该数组可能为空。 id="choix":标识符,用于在每次循环迭代中指定数组中的当前元素。 在第一次迭代中,choix 将表示 listeMultiple[0];在第二次迭代中表示 listeMultiple[1],以此类推。 indexID="index":标识符,用于指定每次循环中数组当前元素的索引。在第一次迭代中,index的值为0;在第二次迭代中,其值为1,依此类推。 位于 <logic:iterate ...> 和 </logic:iterate> 标签之间的代码 HTML 会针对由 (name,property) 对指定的集合中的每个元素重复执行。该代码的动态部分如下:
根据前文所述,在第 i 次迭代(i>=0)中,生成的代码 HTML 等同于以下代码: |
生成一个与应用程序上下文相关的链接,从而无需了解该上下文。此标签生成的代码 HTML 如下: |
6.5. Java类
配置文件 struts-config.xml 引用了两个 Java 类:
<form-bean name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">
...
<action
path="/init"
name="dynaFormulaire"
validate="false"
scope="session"
type="istia.st.struts.formulaire.InitFormulaireAction"
>
类 DynaFormulaire 将包含视图 1(formulaire.jsp)的值。 类 InitFormulaireAction 将处理由 formulaire.jsp 的按钮 [Envoyer] 提交的表单中的值。
6.5.1. 类 DynaFormulaire
要存储表单的值,一个 DynaActionForm 类型的对象就足够了,除非需要重写该类的 reset 或 validate 方法之一。在此处,由于不进行任何数据验证,因此无需重写 validate 方法。 但 reset 方法则需要重写。实际上,DynaFormulaire 对象的字段将从 Web 客户端提交的表单中获取其值。然而,如果某些字段未出现在请求中,它们可能无法获取值。这种情况发生在以下情形中:
- 用户未勾选的复选框
- 下拉列表中选项数量超过1个,或未选中任何选项
对于包含此类组件的表单,reset 方法必须
- 将与该复选框关联的字段值设为“off”
- 将空字符串赋值给与单选列表关联的字段
- 将一个大小为零的字符串数组赋值给与多选列表关联的字段
因此,如果这些字段未从请求中接收值,它们将保留由 reset 赋值的值,该值对应于用户提交表单时组件的状态(复选框未勾选,列表中未选中任何项目)。
类 DynaFormulaire(该类继承自 DynaActionForm)的代码如下:
package istia.st.struts.formulaire;
import org.apache.struts.action.DynaActionForm;
import org.apache.struts.action.ActionMapping;
import javax.servlet.http.HttpServletRequest;
public class DynaFormulaire extends DynaActionForm {
public void reset(ActionMapping mapping, HttpServletRequest request){
// 复选框重置 - 值为 off
set("chk1","off");
set("chk2","off");
set("chk2","off");
// 重置 listeSimple - 空字符串
set("listeSimple","");
// 重置 listeMultiple - 空数组
set("listeMultiple",new String[]{});
}
}
6.5.2. 类 InitFormulaireAction
在文件 struts-config.xml 中,类 InitFormulaireAction 与操作 /init 相关联:
<action
path="/init"
name="dynaFormulaire"
validate="false"
scope="session"
type="istia.st.struts.formulaire.InitFormulaireAction"
>
<forward name="afficherFormulaire" path="/vues/formulaire.jsp"/>
</action>
/init 操作仅在初始化 DynaFormulaire 对象时使用一次。其目的是为下拉列表表单的三个列表(listeSimple、listeMultiple)提供内容。 该内容以三个数组的形式提供,这些数组是 dynaFormulaire 对象的属性:
<form-bean name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">
<form-property name="opt" type="java.lang.String" initial="non"/>
...
<form-property name="valeursCombo" type="java.lang.String[]" />
<form-property name="valeursListeSimple" type="java.lang.String[]" />
<form-property name="valeursListeMultiple" type="java.lang.String[]"/>
</form-bean>
一旦 valeursCombo、valeursListeSimple、valeursListeMultiple 这几个数组被 InitFormulaireAction 初始化后,就不再需要再次初始化。 这是因为对象 dynaFormulaire 被放置在会话中,因此在请求-响应循环中会保留其值。这就是为什么 /init 操作仅执行一次。InitFormulaireAction 的代码如下:
package istia.st.struts.formulaire;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.struts.action.*;
public class InitFormulaireAction
extends Action {
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
// 准备显示表单
// 将表单所需的信息放入其 Bean 中
DynaFormulaire formulaire = (DynaFormulaire) form;
formulaire.set("valeursCombo", getValeurs(5, "combo"));
formulaire.set("valeursListeSimple", getValeurs(7, "simple"));
formulaire.set("valeursListeMultiple", getValeurs(10, "multiple"));
// 控制权交还
return mapping.findForward("afficherFormulaire");
} //执行
// 下拉列表的值列表
private String[] getValeurs(int taille, String label) {
String[] valeurs = new String[taille];
for (int i = 0; i < taille; i++) {
valeurs[i] = label + i;
}
return valeurs;
}
}
- 该类继承自Action类。这是强制要求的。
- Struts控制器通过其execute方法使用Action对象。因此需要重写该方法。该方法接收以下参数:
- ActionMapping mapping:一个表示应用程序配置的对象,位于struts-config.xml
- ActionForm form:与该操作关联的表单(如果操作配置中定义了表单,则通过操作的 name 属性获取)。
- HttpServletRequest request:客户端的请求
- HttpServletResponse:对客户端的响应
- 类 InitFormulaireAction 必须初始化表单 dynaFormulaire。该表单以参数 ActionForm form 的形式传递给 execute 方法。 需要说明的是,dynaFormulaire 属于 DynaFormulaire 类型,该类是从 DynaActionForm 类派生而来的,而 DynaActionForm 类本身又是从 ActionForm 类派生而来的。
- 在 execute 方法中,通过 DynaActionForm 类的 set 方法,为三个字段 valeursCombo、valeursListeSimple、valeursListeMultiple 赋值。 为简化起见,这些值是任意的数组。需要注意的是,set方法仅用于为现有字段赋值,无法用于创建新字段。因此,必须在valeursCombo、 valeursListeSimple、valeursListeMultiple。
- execute方法结束时,会将要显示给客户端的视图键作为响应返回给控制器。在此,afficherFormulaire键在文件struts-config.xml中已被关联到视图/vues/formulaire.jsp。
6.6. 部署
应用程序的目录结构如下:
![]() | ![]() |
![]() | ![]() |


需要提醒的是,上文提到的 ApplicationResources.properties 文件是 struts-bean 标签库所必需的。众所周知,该文件包含应用程序的消息。struts-bean 库可以访问这些消息。在此,我们的应用程序未定义任何消息。因此,ApplicationResources.properties 文件虽然存在,但内容为空。
6.7. 结论
在本课中,我们详细介绍了如何管理 HTML 表单的各个组件。现在,我们可以在 Struts 应用程序中使用复杂的表单了。






