在我早前的文章《转换器(Converter)——Struts 2.0中的魔术师》(以下简称为《转》)中,提及在Struts 1.x中实现批量封装对象,并不是一件容易的事,这需要一些技巧。昨天,有一位同事又和我讨论起这个问题,所以鉴于此场景(scenario)较为普遍,我决定写一篇有关的文章。
本文使用《转》中的最后一个例子作为应用场景,即是批量发布产品信息。页面输出如下图所示:
图1 发布产品
图2 查看产品
首先创建代表产品的类tipsAndTricks.Product,代码如下:
与《转》例中的Product不同的是,本例子中的dateOfProduction属性使用了java.sql.Date,而不是java.util.Date。这是因为Struts 1.x不支持请求参数到java.util.Date的转换,归根到底是由于org.apache.commons.beanutils.ConvertUtilsBean.convert()不支持关于java.util.Date的转换。另外,值得注意的是common-beanutils是通过java.sql.Date.valueOf()方法工作的,所以在页面输入的字符串的格式必须为“yyyy-MM-dd”。
实现上述功能大概有三种方法,下面我会分别对这三种方法进行详细的讲述。
首先,让我们来看一下Struts的配置文件WEB-INF/struts-config.xml,内容如下:
我想这些配置应该用不着怎么解释了,有Struts 1.x验证的朋友对此都不会陌生。因此,接下来创建/addProducts.jsp文件,代码如下:
例中,我使用了JSTL 1.1,如果大家还没有尝试过使用JSP 2.0的JSTL和EL,建议大家去看看相关文章。上面的<c:forEach />的作用是到dynaProductsForm的map属性中取出products数组,并对其进行遍历,再依靠<html:text />标志将products的元素的属性以输入框的形式输出。<html:text />标志的属性indexed="true"则表示在输出HTML时,将<input>的命名为类似products[0].name的名字。
然后,再创建/viewProducts.jsp页面,内容如下:
我想这份也不多作说明。不过大家可以通过上述代码看出使用JSTL + EL的确比Struts 1.x的logic + bean要方便和简洁。不仅如此,EL还支持一定的运算符和函数操作。
最后是建立Action文件tipsAndTricks.BatchWrappingWithArrayAction,代码如下:
此Action将动态表单传过来的products数组放到request中,转到/viewProducts.jsp。发布运行应用程序,在浏览器的地址栏中输入:http://localhost:8080/Struts1_Batch/addProducts.jsp。效果请参考如图1、图2。
方法一虽然简单,但是有一个明显的缺点——数组的长度已经固定,故我们不能在运行时通过程序设置对象数量。下面将要介绍的方法可以很好地解决这个问题。
首先,我们要创建类tipsAndTricks.AutoInitArrayList,代码如下:
AutoInitArrayList继承ArrayList并重载get()方法,作用就是在Struts 1.x框架调用这个方法时,如果index超出列表大小,则会实例化新项放到列表中,避免出现(IndexOutOfBoundsException)异常。
接着,让我们看Struts的配置,内容如下:
然后,创建表单类tipsAndTricks.NormalProductsForm,代码如下:
接下来是Action类tipsAndTricks.BatchWrappingNormalAction,代码如下:
基本上与方法一的Action一样。下面,再看看新的输入文件/addProducts2.jsp,内容如下:
/addProducts2.jsp主要作用组装<input>的元素名称,Struts 1.x对名称格式类似“xxx[9].xx”的请求,会进行封装。发布运行应用程序,在浏览器的地址栏中输入:http://localhost:8080/Struts1_Batch/addProducts2.jsp。效果请参考如图1、图2。
两种方法各有优缺点,选择原则是如果不需要动态设置元素个数,则使用方法一,否则请使用方法二。