本篇教程中,将探索使用Struts2来做更多的表单提交信息处理。我们会讲解如何使用一个Java模型类来存储表单输入,以及如何构建Struts2 表单来配合模型类。 本篇教程中提供的代码可以添加到教程《Struts2动作类代码编写》的示例,活着也可以从github网站上下载完整示例。
在本篇教程里,我们假设需要提供一个表单,让用户为了抽奖活动而提交用于注册的信息。我们的业务规则规定,用户必须提供他/她的姓、名、电子邮箱地址,以及年龄。 为了封装这些数据,我们讲使用一个简单的Java类,遵循基础的Java Bean规范(为每个实例区都提供公开的set/get方法)。如果你一路忠诚拜读系列教程至此,可以把下面这个类添加到《Struts2动作类代码编写》示例代码中的org.apache.struts.register.model包中。
注意上面这个类中的几个点。每个实例域(成员变量)都有一个公开的set/get方法。age属性是integer类型。定义了一个公开的toString()方法,返回一个代表对象状态的字符串。由于我们没有指定构造函数,Java回提供一个默认的构造函数,会用对应的null值设置所有的实例域。
为了收集上述信息,我们将使用一个Struts2表单。构建这个表单时,需要引入的关键概念是:把每个表单域对应到Person类型对象的一个特定实例域。先整体地看一下表单,然后再讨论关键点。在src/main/webapp路径下创建名为register.jsp的页面。
由于我们使用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类中,注意到我们已经声明了一个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。
如果读者没想起来Struts2的property和url标签是如何工作的,回头看看《使用Struts2标签》教程。
为了指定表单提交页、Struts2动作类、以及success视图页面之间的关系,现在要在struts.xml中添加一个动作结点。将以下动作结点添加到struts.xml中,在hello动作之后、包封闭结点之前。
以上动作告诉Struts2,当register动作被提供来调用Register类的execute方法,如果方法返回了“success”这个结果,那么把thankyou.jsp这个页面返回给浏览器。 注意,咱并不需要告诉Struts2任何关于处理表单的过程。表单域输入值传输到personBean实例会自动地发生,因为我们遵循了“将表单域命名与personBean属性名一致”的规则(例如表单域中的lastName与personBean.lastName)。
添加以下链接到index.jsp,这样用户就可以找到注册页面咯。
若万事俱备,就可以运行这个应用,并且再浏览器中打开index.action。页面里应当有一个注册的链接。点击这个链接,应能看到注册页面。 填写表单,并且点击“提交”按钮。 应当能看到thankyou.jsp页面。