在我早前的文章《轉換器(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”。
實現上述功能大概有三種方法,下面我會分別對這三種方法進行詳細的講述。
方法一、動態表單(Dynamic Actoin Form)+ 數組
首先,讓我們來看一下Struts的配置文件WEB-INF/struts-config.xml,內容如下:
<! DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN" "http://struts.apache.org/dtds/struts-config_1_2.dtd" >
< struts-config >
< data-sources />
< form-beans >
< form-bean name ="dynaProductsForm"
type ="org.apache.struts.action.DynaActionForm" >
< form-property name ="products"
type ="tipsAndTricks.Product[]" size ="3" />
</ form-bean >
</ form-beans >
< global-exceptions />
< global-forwards />
< action-mappings >
< action attribute ="dynaProductsForm" input ="/addProducts.jsp"
name ="dynaProductsForm" path ="/batchWrappingWithArray"
scope ="request" type ="tipsAndTricks.BatchWrappingWithArrayAction"
validate ="false" >
< forward name ="success" path ="/viewProducts.jsp" />
</ action >
</ action-mappings >
< message-resources parameter ="tipsAndTricks.ApplicationResources" />
</ struts-config >
我想這些配置應該用不著怎么解釋了,有Struts 1.x驗證的朋友對此都不會陌生。因此,接下來創建/addProducts.jsp文件,代碼如下:
<% @ taglib uri = " http://struts.apache.org/tags-html " prefix = " html " %>
<% @ taglib uri = " http://java.sun.com/jsp/jstl/core " prefix = " c " %>
<! DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" >
< html >
< head >
< title > Add Products </ title >
</ head >
< body >
< html:form action ="/batchWrappingWithArray" method ="post" >
< table border ="0" >
< tr style ="background-color:powderblue; font-weight:bold;" >
< td > Product Name </ td >
< td > Price </ td >
< td > Date of production </ td >
</ tr >
< c:forEach var ="products" items ="${dynaProductsForm.map.products}" >
< tr >
< td >< html:text indexed ="true" name ="products" property ="name" /></ td >
< td >< html:text indexed ="true" name ="products" property ="price" /></ td >
< td >< html:text indexed ="true" name ="products" property ="dateOfProduction" /></ td >
</ tr >
</ c:forEach >
< tr >
< td colspan ="3" >< html:submit /></ td >
</ tr >
</ table >
</ html:form >
</ body >
</ html >
例中,我使用了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頁面,內容如下:
<% @ taglib uri = " http://java.sun.com/jsp/jstl/core " prefix = " c " %>
<! DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" >
< html >
< head >
< title > View Products </ title >
</ head >
< body >
< table border ="0" >
< tr style ="background-color:powderblue; font-weight:bold;" >
< td > Product Name </ td >
< td > Price </ td >
< td > Date of production </ td >
</ tr >
< c:forEach var ="product" items ="${products}" >
< tr >
< td > ${product.name} </ td >
< td > ${product.price} </ td >
< td > ${product.dateOfProduction} </ td >
</ tr >
</ c:forEach >
</ table >
</ body >
</ html >
我想這份也不多作說明。不過大家可以通過上述代碼看出使用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。
![]() |
在/addProducts.jsp的“Date of production”必須以(yyyy-MM-dd)的形式正確填寫,且不能為空。 |
方法二、表單(Actoin Form)+ 列表(List)
方法一雖然簡單,但是有一個明顯的缺點——數組的長度已經固定,故我們不能在運行時通過程序設置對象數量。下面將要介紹的方法可以很好地解決這個問題。
首先,我們要創建類tipsAndTricks.AutoInitArrayList,代碼如下:

























AutoInitArrayList繼承ArrayList并重載get()方法,作用就是在Struts 1.x框架調用這個方法時,如果index超出列表大小,則會實例化新項放到列表中,避免出現(IndexOutOfBoundsException)異常。
接著,讓我們看Struts的配置,內容如下:
<! DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN" "http://struts.apache.org/dtds/struts-config_1_2.dtd" >
< struts-config >
< data-sources />
< form-beans >
< form-bean name ="dynaProductsForm"
type ="org.apache.struts.action.DynaActionForm" >
< form-property name ="products"
type ="tipsAndTricks.Product[]" size ="3" />
</ form-bean >
< form-bean name ="normalProductsForm"
type ="tipsAndTricks.NormalProductsForm" />
</ form-beans >
< global-exceptions />
< global-forwards />
< action-mappings >
< action attribute ="dynaProductsForm" input ="/addProducts.jsp"
name ="dynaProductsForm" path ="/batchWrappingWithArray"
scope ="request" type ="tipsAndTricks.BatchWrappingWithArrayAction"
validate ="false" >
< forward name ="success" path ="/viewProducts.jsp" />
</ action >
< action attribute ="normalProductsForm" input ="/addProducts.jsp"
name ="normalProductsForm" path ="/batchWrappingNormal" scope ="request"
type ="tipsAndTricks.BatchWrappingNormalAction" validate ="false" >
< forward name ="success" path ="/viewProducts.jsp" />
</ action >
</ action-mappings >
< message-resources parameter ="tipsAndTricks.ApplicationResources" />
</ struts-config >
然后,創建表單類tipsAndTricks.NormalProductsForm,代碼如下:

















接下來是Action類tipsAndTricks.BatchWrappingNormalAction,代碼如下:






















基本上與方法一的Action一樣。下面,再看看新的輸入文件/addProducts2.jsp,內容如下:
<% @ taglib uri = " http://struts.apache.org/tags-html " prefix = " html " %>
<% @ taglib uri = " http://java.sun.com/jsp/jstl/core " prefix = " c " %>
<! DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" >
< html >
< head >
< title > Add Products </ title >
</ head >
< body >
< html:form action ="/batchWrappingNormal" method ="post" >
< table border ="0" >
< tr style ="background-color:powderblue; font-weight:bold;" >
< td > Product Name </ td >
< td > Price </ td >
< td > Date of production </ td >
</ tr >
< c:forEach begin ="0" end ="2" var ="i" >
< tr >
< td >< input name ="products[${i}].name" /></ td >
< td >< input name ="products[${i}].price" /></ td >
< td >< input name ="products[${i}].dateOfProduction" /></ td >
</ tr >
</ c:forEach >
< tr >
< td colspan ="3" >< html:submit /></ td >
</ tr >
</ table >
</ html:form >
</ body >
</ html >
/addProducts2.jsp主要作用組裝<input>的元素名稱,Struts 1.x對名稱格式類似“xxx[9].xx”的請求,會進行封裝。發布運行應用程序,在瀏覽器的地址欄中輸入:http://localhost:8080/Struts1_Batch/addProducts2.jsp。效果請參考如圖1、圖2。
總結
兩種方法各有優缺點,選擇原則是如果不需要動態設置元素個數,則使用方法一,否則請使用方法二。