介绍
本篇教程中,将探索使用Struts2来做更多的表单提交信息处理。我们会讲解如何使用一个Java模型类来存储表单输入,以及如何构建Struts2 表单来配合模型类。
本篇教程中提供的代码可以添加到教程《Struts2动作类代码编写》的示例,活着也可以从github网站上下载完整示例。
表单和Java模型类
在本篇教程里,我们假设需要提供一个表单,让用户为了抽奖活动而提交用于注册的信息。我们的业务规则规定,用户必须提供他/她的姓、名、电子邮箱地址,以及年龄。
为了封装这些数据,我们讲使用一个简单的Java类,遵循基础的Java Bean规范(为每个实例区都提供公开的set/get方法)。如果你一路忠诚拜读系列教程至此,可以把下面这个类添加到《Struts2动作类代码编写》示例代码中的org.apache.struts.register.model包中。
Person.java
public class Person {
private String firstName;
private String lastName;
private String email;
private int age;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "First Name: " + getFirstName() + " Last Name: " + getLastName() +
" Email: " + getEmail() + " Age: " + getAge() ;
}
}
注意上面这个类中的几个点。每个实例域(成员变量)都有一个公开的set/get方法。age属性是integer类型。定义了一个公开的toString()方法,返回一个代表对象状态的字符串。由于我们没有指定构造函数,Java回提供一个默认的构造函数,会用对应的null值设置所有的实例域。
表单结构
为了收集上述信息,我们将使用一个Struts2表单。构建这个表单时,需要引入的关键概念是:把每个表单域对应到Person类型对象的一个特定实例域。先整体地看一下表单,然后再讨论关键点。在src/main/webapp路径下创建名为register.jsp的页面。
register.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<%@ page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-i" />
</head>
<body>
<h3>完成填表即可注册抽奖!</h3>
<s:form action="register">
<s:textfield name="personBean.firstName" label="名"/>
<s:textfield name="personBean.lastName" label="姓"/>
<s:textfield name="personBean.email" label="电子邮箱" /s>
<s:textfield name="personBean.age" label="年龄" />
<s:submit/>
</s:form>
</body>
</html>
由于我们使用Struts2标签,所以在页面最上方需要写Struts标签库的声明。
Struts2表单将会向一个名为register的动作提交,所以我们需要在struts.xml文件中定义这个动作。
注意四个Struts2的textfield标签。每个标签有一个name值,包括了一个Person类的属性(例如firstName)。这个name属性的值也有一个指向名为personBean对象的引用。这个对象的类型是Person。当我们创建控制这个表单提交行为的动作类,我们要在动作类中指定对象(见下方)。
完整的name值,personBean.firstName,引导Struts2使用这个textfield的输入值作为参数提供给personBean对象的setFirstName方法。这样如果用户在标签为“名”的textfield中输入了“Bruce”,那么personBean的firstName实例域,值就是“Bruce”。
注意我们对Person类的每个实例域都有了一个Struts2的textfield。记住,Person类的年龄属性是integer类型。所有表单域的输入值都是字符串。当调用personBean对象的setAge方法时,Struts2会自动地将用户为年龄表单域输入的字符串类型值(“25”)转换为25。
创建动作类来掌控表单提交
当用户点击上面表单的“提交”按钮,“register”这个动作以及表单数据会被发送到Struts2框架。我们需要一个动作类来处理这个表单。回顾一下前面的教程吧,自己编写的动作类应当继承Struts2的ActionSupport类。
这里是本示例所用的动作类。请将它打包到org.apache.struts.register.action。
Register.java Struts 2 动作类
package org.apache.struts.register.action;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts.register.model.Person;
public class Register extends ActionSupport{
private static final long serialVersionUID = 1L;
private Person personBean;
public String execute() throws Exception {
//call Service class to store personBean's state in database
return SUCCESS;
}
public Person getPersonBean() {
return personBean;
}
public void setPersonBean(Person person) {
personBean = person;
}
}
在Register类中,注意到我们已经声明了一个Person类型、名为personBean的属性(译者注:成员变量),并且Register对这个实例拥有公开的get和set方法。
Register类也重写了execute方法。execute是我们会在struts.xml中指定调用来响应register动作的方法。在这个例子中,execute方法只是返回了字符串常量SUCCESS(继承自ActionSupport类)。但在真正的应用里,在execute方法中我们应当调用其它类(服务实体)来执行表单的业务处理,例如存储用户的输入到数据仓库。
在Register动作类中声明的,Person类型的personBean这个实例,对应到我们在表单中name值为personBean的textfield。当表单被提交,Struts2框架将会检查动作类并且寻找名为personBean的实例;会使用Person类的默认构造函数创建这个实例。然后,对每个拥有name值为personBean.某某某(例如personBean.firstName)的表单域,将会为这个属性调用personBean的公开set方法,并且将表单域的值(即用户的输入)传递过去。这些都发生在execute方法执行之前。
当Struts2执行Register的execute方法,Register类的personBean实例的实例域(译者注:成员变量)现在拥有了对应自己这个实例的值,等于用户在对应的表单域里输入的值。
通过使用一个Java模型类来封装表单提供的数据,我们就不必在动作类(Register)中,为每个表单域都提供单独的属性(及公开的set/get方法们)。 【译者注:目前这里的“模型类”应该是指Person类。“封装对应表单域的属性和set/get方法”是一定要存在的;要么像先前示例那样在动作类中书写,要么像本例这样放在Person这个模型类里,让动作类不体现这些细节。】
在视图中添加结果
当execute方法返回了“SUCCESS”,我们希望为用户的注册显示一个简单的感谢页面。在src/main/webapp路径下添加thankyou.jsp。
thankyou.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Registration Successful</title>
</head>
<body>
<h3>Thank you for registering for a prize.</h3>
<p>Your registration information: <s:property value="personBean" /> </p>
<p><a href="<s:url action='index' />" >Return to home page</a>.</p>
</body>
</html>
如果读者没想起来Struts2的property和url标签是如何工作的,回头看看《使用Struts2标签》教程。
在struts.xml中创建动作映射
为了指定表单提交页、Struts2动作类、以及success视图页面之间的关系,现在要在struts.xml中添加一个动作结点。将以下动作结点添加到struts.xml中,在hello动作之后、包封闭结点之前。
struts.xml中的action结点
<action name="register" class="org.apache.struts.register.action.Register" method="execute">
<result name="success">/thankyou.jsp</result>
</action>
以上动作告诉Struts2,当register动作被提供来调用Register类的execute方法,如果方法返回了“success”这个结果,那么把thankyou.jsp这个页面返回给浏览器。
注意,咱并不需要告诉Struts2任何关于处理表单的过程。表单域输入值传输到personBean实例会自动地发生,因为我们遵循了“将表单域命名与personBean属性名一致”的规则(例如表单域中的lastName与personBean.lastName)。
创建到Register.jsp的链接
添加以下链接到index.jsp,这样用户就可以找到注册页面咯。
到register.jsp的链接
<p>请先<a href="register.jsp">注册</a>就能抽奖咯</p>
运行示例
若万事俱备,就可以运行这个应用,并且再浏览器中打开index.action。页面里应当有一个注册的链接。点击这个链接,应能看到注册页面。
填写表单,并且点击“提交”按钮。
应当能看到thankyou.jsp页面。