站内搜索: 请输入搜索关键词

当前页面: 开发资料首页Java 专题学习Struts 2.0系列(5) 转换器--Struts 2_0中的魔术师

学习Struts 2.0系列(5) 转换器--Struts 2_0中的魔术师

摘要: 学习Struts 2.0系列(5) 转换器--Struts 2_0中的魔术师
器中键入http://localhost:8080/Struts2_Converter/HelloWorld.action,输出页面如图2所示:


图2 HelloWorld英文输出

在Locale输入框中输入“zh_CN”,按“Submit”提交,出现如图3所示页面:


图3 HelloWorld中文输出

上述例子中,Locale文本输入框对应是Action中的类型为java.util.Locale的属性loc,所以需要创建一个自定义转变器实现两者间的转换。所有的Struts 2.0中的转换器都必须实现ognl.TypeConverter接口。 为了简单起见,OGNL包也为你提供了ognl.DefaultTypeConverter类去帮助您实现转换器。在例子中,LocaleConverter继承了ognl.DefaultTypeConverter,重载了其方法原型为“public Object convertValue(Map context, Object value, Class toType)”的方法。下面简单地介绍一下函数的参数:

  1. context——用于获取当前的ActionContext
  2. value——需要转换的值
  3. toType——需要转换成的目标类型
实现转换器,我们需要通过配置告诉Struts 2.0。我们可以通过以下两种方法做到这点:
  1. 配置全局的类型转换器,也即是上例的做法——在源代码文件夹下,新建一个名为“xwork-conversion.properties”的配置文件,并在文件中加入“待转换的类型的全名(包括包路径和类名)=转换器类的全名”对;
  2. 应用于某个特定类的类型转换器,做法为在该类的包中添加一个格式为“类名-conversion.properties”的配置文件,并在文件中加入“待转换的属性的名字=转换器类的全名”对。上面的例子也可以这样配置——在源代码文件夹的tutorial包下新建名为“HelloWorld-conversion.properties”文件,并在其中加入“loc=tutorial.LocaleConverter”。
<table style="BORDER-RIGHT: #f0c000 1px solid; BORDER-TOP: #f0c000 1px solid; MARGIN-TOP: 8px; MARGIN-BOTTOM: 8px; BORDER-LEFT: #f0c000 1px solid; BORDER-BOTTOM: #f0c000 1px solid; BACKGROUND-COLOR: #ffffce; TEXT-ALIGN: left" cellSpacing=0 cellPadding=0 border=0> <tr> <td style="PADDING-RIGHT: 4px; PADDING-LEFT: 4px; PADDING-BOTTOM: 4px; PADDING-TOP: 4px"></td> <td style="PADDING-RIGHT: 4px; PADDING-LEFT: 4px; PADDING-BOTTOM: 4px; PADDING-TOP: 4px; FONT-FAMILY: 华文仿宋">在继承DefaultTypeConverter时,如果是要将value转换成其它非字符串类型时,要记住value是String[]类型,而不是String类型。它是通过request.getParameterValues(String arg)来获得的,所以不要试图将其强行转换为String类型。 </td> </tr> </table>

已有的转换器

对于一此经常用到的转换器,如日期、整数或浮点数等类型,Struts 2.0已经为您实现了。下面列出已经实现的转换器。

  1. 预定义类型,例如int、boolean、double等;
  2. 日期类型, 使用当前区域(Locale)的短格式转换,即DateFormat.getInstance(DateFormat.SHORT);
  3. 集合(Collection)类型, 将request.getParameterValues(String arg)返回的字符串数据与java.util.Collection转换;
  4. 集合(Set)类型, 与List的转换相似,去掉相同的值;
  5. 数组(Array)类型, 将字符串数组的每一个元素转换成特定的类型,并组成一个数组。
对于已有的转换器,大家不必再去重新发明轮子。Struts在遇到这些类型时,会自动去调用相应的转换器。

批量封装对象(Bean)

不知道大家是否遇过这种情况,在一个页面里同时提交几个对象。例如,在发布产品的页面,同时发布几个产品。我在之前一个项目就遇到过这种需求,当时用的是Struts 1.x。那是一个痛苦的经历,我在Google搜了很久都没有理想的结果。幸运的是,在Struts 2.0中这种痛苦将一去不复返。下面我就演示一下如何实现这个需求。

首先,在源代码文件夹下的tutorial包中新建Product.java文件,内容如下:

package tutorial;

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;
}

}

然后,在同上的包下添加ProductConfirm.java类,代码如下:

package tutorial;

import java.util.List;

import com.opensymphony.xwork2.ActionSupport;

publicclass ProductConfirm extends ActionSupport {
public List products;

public List getProducts() {
return products;
}


publicvoid setProducts(List products) {
this.products = products;
}


@Override
public String execute() {
for(Product p : products) {
System.out.println(p.getName() + " | "+ p.getPrice() +" | " + p.getDateOfProduction());
}

return SUCCESS;
}

}

接看,在同上的包中加入ProductConfirm-conversion.properties,代码如下:

Element_products=tutorial.Product

再在struts.xml文件中配置ProductConfirm Action,代码片段如下:


/ShowProducts.jsp

在WEB文件夹下新建AddProducts.jsp,内容如下:

<%@ page contentType="text/html; charset=UTF-8"%>
<%@taglib prefix="s" uri="/struts-tags"%>

<head>
Hello World
</head>
<body>

<table>
<tr style="background-color:powderblue; font-weight:bold;">
<td>Product Name</td>
<td>Price</td>
<td>Date of production</td>
</tr>

<tr>
<td></td>
<td></td>
<td></td>
</tr>

<tr>
<td colspan="3"></td>
</tr>
</table>

</body>

在同样的文件夹下创建ShowProducts.jsp,内容如下:

<%@ page contentType="text/html; charset=UTF-8"%>
<%@taglib prefix="s" uri="/struts-tags"%>

<head>
Hello World
</head>
<body>
<table>
<tr style="background-color:powderblue; font-weight:bold;">
<td>Product Name</td>
<td>Price</td>
<td>Date of production</td>
</tr>

<tr>
<td></td>
<td>$</td>
<td></td>
</tr>

</table>
</body>

发布运行应用程序,在浏览器中键入http://localhost:8080/Struts2_Converter/AddProducts.jsp,出现如图4所示页面:


图4 添加产品页面

按图4所示,填写表单,按“Submit”提交,出现图5所示页面:


图5 查看产品页面

查看服务器的控制台,有如下输出:

Expert One-on-One J2EE Development without EJB | 39.99 | Mon Jun 2100:00:00 CST 2004
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

上面的代码并不复杂,但有几点需要说明:

  1. ProductConfirm文件中的for(Product p : productes)的写法是J2SE 5.0中的新特性,作用遍历products列表;
  2. List也是J2SE 5.0的才有的泛型(Generic);
  3. ProductConfirm-conversion.properties中“Element_products=tutorial.Product”是告诉Struts 2.0列表products的元素的类型为Product,而不是定义转换器;
  4. 在AddProducts.jsp的的name为“%{'products['+#stat.index+'].name'}”,%{exp}格式表示使用OGNL表达式,上述表达式的相当于<%= "products[" + stat.index + "].name" %>,至于标志的用法可以参考我之前的文章《常用的Struts 2.0的标志(Tag)介绍》。

转换错误处理

不知道大家在运行上面的例子时,有没有填错日期或数字情况,又或者您有没有思考过这种情况?如果还没有尝试的朋友可以试一下,在第一行的Price和Date of production中输入英文字母,然后按“Submit”提交。你会看到页面为空白,再看一下服务器的控制台输出,有如下语句: 警告: No result defined for action tutorial.ProductConfirm and result input,它提示我们没有为Action定义输入结果,所以,我们应该在源代码文件夹下的struts.xml中的ProductConfirm Action中加入以下代码:

/AddProducts.jsp

重新加载应用程序,刷新浏览器重新提交请求,这时页面返回AddProducts.jsp,格式错误的输入框的值被保留,如下图6所示:


图6 没有提示的错返回页面

当然,我们还可以在页面上加上错误提示信息,通过在AddProducts.jsp的“<body>”后,加入下面代码可以实现:



刷新浏览器,重新提交请求,出现如图7所示页面:


图7 带提示的错返回页面

以上的功能的都是通过Struts 2.0里的一个名为conversionError的拦截器(interceptor)工作,它被注册到默认拦截器栈(default interceptor stack)中。Struts 2.0在转换出错后,会将错误放到ActionContext中,在conversionError的作用是将这些错误封装为对应的项错误(field error),因此我们可以通过来将其在页面上显示出来。另外,大家看第二和第三行的Price都被赋为0.0的值,而第一行则保留其错误值。这同样是conversionError的功劳——没有出错的行调用的products[index].price(默认值为0.0),而出错的行则会被赋为页面所提交的错误值,这样可以提供更好的用户体验。

总结

Struts 2.0的转换器简化的WEB应用程序的模型,为我们的编程带来极大的方便。

</td> </tr> <tr>


↑返回目录
前一篇: 学习Struts 2.0系列(4) 在Struts 2.0中国际化您的应用程序
后一篇: 中文Struts1.1快速学习指南