在我已往的Struts 1.x项目经验中,有个问题不时的出现——在创建FormBean时,对于某个属性到底应该用String还是其它类型?
图1 UI与服务器对象关系
<input type="text" value="<%= DateFormat.getInstance(DateFormat.SHORT).format(birthday) %>" />
转换器——Hello World
import java.util.Locale;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.LocalizedTextUtil;
public class HelloWorld extends ActionSupport {
private String msg;
private Locale loc = Locale.US;
public String getMsg() {
return msg;
}
public Locale getLoc() {
return loc;
}
public void setLoc(Locale loc) {
this .loc = loc;
}
@Override
public String execute() {
// LocalizedTextUtil是Struts 2.0中国际化的工具类,<s:text>标志就是通过调用它实现国际化的
msg = LocalizedTextUtil.findDefaultText( " HelloWorld " , loc);
return SUCCESS;
}
}
< action name ="HelloWorld" class ="tutorial.HelloWorld" >
< result > /HelloWorld.jsp </ result >
</ action >
</ package >
< %@taglib prefix ="s" uri ="/struts-tags" % >
< html >
< head >
< title > Hello World </ title >
</ head >
< body >
< s:form action ="HelloWorld" theme ="simple" >
Locale: < s:textfield name ="loc" /> < s:submit />
</ s:form >
< h2 >< s:property value ="msg" /></ h2 >
</ body >
</ html >
import java.util.Locale;
import java.util.Map;
public class LocaleConverter extends ognl.DefaultTypeConverter {
@Override
public Object convertValue(Map context, Object value, Class toType) {
if (toType == Locale. class ) {
String locale = ((String[]) value)[ 0 ];
return new Locale(locale.substring( 0 , 2 ), locale.substring( 3 ));
} else if (toType == String. class ) {
Locale locale = (Locale) value;
return locale.toString();
}
return null ;
}
}
图2 HelloWorld英文输出
图3 HelloWorld中文输出
- context——用于获取当前的ActionContext
- value——需要转换的值
- toType——需要转换成的目标类型
- 配置全局的类型转换器,也即是上例的做法——在源代码文件夹下,新建一个名为“xwork-conversion.properties”的配置文件,并在文件中加入“待转换的类型的全名(包括包路径和类名)=转换器类的全名”对;
- 应用于某个特定类的类型转换器,做法为在该类的包中添加一个格式为“类名-conversion.properties”的配置文件,并在文件中加入“待转换的属性的名字=转换器类的全名”对。上面的例子也可以这样配置——在源代码文件夹的tutorial包下新建名为“HelloWorld-conversion.properties”文件,并在其中加入“loc=tutorial.LocaleConverter”。
在继承DefaultTypeConverter时,如果是要将value转换成其它非字符串类型时,要记住value是String[]类型,而不是String类型。它是通过request.getParameterValues(String arg)来获得的,所以不要试图将其强行转换为String类型。 |
已有的转换器
- 预定义类型,例如int、boolean、double等;
- 日期类型, 使用当前区域(Locale)的短格式转换,即DateFormat.getInstance(DateFormat.SHORT);
- 集合(Collection)类型, 将request.getParameterValues(String arg)返回的字符串数据与java.util.Collection转换;
- 集合(Set)类型, 与List的转换相似,去掉相同的值;
- 数组(Array)类型, 将字符串数组的每一个元素转换成特定的类型,并组成一个数组。
批量封装对象(Bean)
import java.util.Date;
publicclass Product {
private String name;
privatedouble price;
private Date dateOfProduction;
public Date getDateOfProduction() {
return dateOfProduction;
}
publicvoid setDateOfProduction(Date dateOfProduction) {
this.dateOfProduction = dateOfProduction;
}
public String getName() {
return name;
}
publicvoid setName(String name) {
this.name = name;
}
publicdouble getPrice() {
return price;
}
publicvoid setPrice(double price) {
this.price = price;
}
}
import java.util.List;
import com.opensymphony.xwork2.ActionSupport;
publicclass ProductConfirm extends ActionSupport {
public List<Product> products;
public List<Product> getProducts() {
return products;
}
publicvoid setProducts(List<Product> products) {
this.products = products;
}
@Override
public String execute() {
for(Product p : products) {
System.out.println(p.getName() + " | "+ p.getPrice() +" | " + p.getDateOfProduction());
}
return SUCCESS;
}
}
<result>/ShowProducts.jsp</result>
</action>
<%@taglib prefix="s" uri="/struts-tags"%>
<html>
<head>
<title>Hello World</title>
</head>
<body>
<s:form action="ProductConfirm" theme="simple">
<table>
<tr style="background-color:powderblue; font-weight:bold;">
<td>Product Name</td>
<td>Price</td>
<td>Date of production</td>
</tr>
<s:iterator value="new int[3]" status="stat">
<tr>
<td><s:textfield name="%{'products['+#stat.index+'].name'}"/></td>
<td><s:textfield name="%{'products['+#stat.index+'].price'}"/></td>
<td><s:textfield name="%{'products['+#stat.index+'].dateOfProduction'}"/></td>
</tr>
</s:iterator>
<tr>
<td colspan="3"><s:submit /></td>
</tr>
</table>
</s:form>
</body>
</html>
<%@taglib prefix="s" uri="/struts-tags"%>
<html>
<head>
<title>Hello World</title>
</head>
<body>
<table>
<tr style="background-color:powderblue; font-weight:bold;">
<td>Product Name</td>
<td>Price</td>
<td>Date of production</td>
</tr>
<s:iterator value="products" status="stat">
<tr>
<td><s:property value="name"/></td>
<td>$<s:property value="price"/></td>
<td><s:property value="dateOfProduction"/></td>
</tr>
</s:iterator>
</table>
</body>
</html>
图4 添加产品页面
图5 查看产品页面
Pro Spring | 32.99 | Mon Jan 3100:00:00 CST 2005
Core J2EE Patterns: Best Practices and Design Strategies, Second Edition | 34.64 | Sat May 1000:00:00 CST 2003
- ProductConfirm文件中的for(Product p : productes)的写法是J2SE 5.0中的新特性,作用遍历products列表;
- List<Product>也是J2SE 5.0的才有的泛型(Generic);
- ProductConfirm-conversion.properties中“Element_products=tutorial.Product”是告诉Struts 2.0列表products的元素的类型为Product,而不是定义转换器;
- 在AddProducts.jsp的<s:textfield>的name为“%{'products['+#stat.index+'].name'}”,%{exp}格式表示使用OGNL表达式,上述表达式的相当于<%= "products[" + stat.index + "].name" %>,至于<s:iterator>标志的用法可以参考我之前的文章《常用的Struts 2.0的标志(Tag)介绍》。
转换错误处理
图6 没有提示的错返回页面
<s:fielderror />
</div>
图7 带提示的错返回页面
总结
|