转换器(Converter)—Struts 2.0中的魔术师
2008-07-16 13:15
435 查看
本系列文章导航
为Struts 2.0做好准备Struts 2的基石——拦截器(Interceptor)
常用的Struts 2.0的标志(Tag)介绍
在Struts 2.0中国际化(i18n)您的应用程序
转换器(Converter)—Struts 2.0中的魔术师
在Struts 2.0中实现表单数据校验(Validation)
在我已往的Struts 1.x项目经验中,有个问题不时的出现——在创建FormBean时,对于某个属性到底应该用String还是其它类型?
开发Web应用程序与开发传统桌面应用程序不同,Web应用程序实际上是分布个不同的主机(当然也可以同一个主机,不过比较少见)上的两个进程之间互交。这种互交建立在HTTP之上,它们互相传递是都是字符串。换句话说, 服务器可以的接收到的来自用户的数据只能是字符串或字符数组,而在服务器上的对象中,这些数据往往有多种不同的类型,如日期(Date),整数(int),浮点数(float)或自定义类型(UDT)等,如图1所示。因此,我们需要服务器端将字符串转换为适合的类型。
图1 UI与服务器对象关系
同样的问题也发生在使用UI展示服务器数据的情况。HTML的Form控件不同于桌面应用程序可以表示对象,其值只能为字符串类型,所以我们需要通过某种方式将特定对象转换成字符串。
要实现上述转换,Struts 2.0中有位魔术师可以帮到你——Converter。有了它,你不用一遍又一遍的重复编写诸如此类代码:
Date birthday = DateFormat.getInstance(DateFormat.SHORT).parse(strDate);
<input type="text" value="<%= DateFormat.getInstance(DateFormat.SHORT).format(birthday) %>" />
好了,现在让我们来看一个例子。
转换器——Hello World
在我的上一篇文章《在Struts 2.0中国际化(i18n)您的应用程序》的最后我举了一个可以让用户方便地切换语言的例子,下面例子与其相似,但实现方法不同。
首先,如《在Struts 2.0中国际化(i18n)您的应用程序
》的第一个例子一样,创建和配置默认的资源文件;
接着,新建源代码文件夹下的tutorial包创建HelloWorld.java文件,代码如下:
package
tutorial;
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;
}
}
然后,在源代码文件夹下的struts.xml加入如下代码新建Action:
<
package
name
="ConverterDemo"
extends
="struts-default"
>
<
action
name
="HelloWorld"
class
="tutorial.HelloWorld"
>
<
result
>
/HelloWorld.jsp
</
result
>
</
action
>
</
package
>
再在Web文件夹下,新建 HelloWorld.jsp,代码如下:
<
%@ page
contentType
="text/html; charset=UTF-8"
%
>
<
%@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
>
接下来,在源代码文件夹的tutorial包中新建LocaleConverter.java文件,代码如下:
package
tutorial;
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
;
}
}
再接下来,在源代码文件夹下新建xwork-conversion.properties,并在其中添加如下代码:
java.util.Locale
=
tutorial.LocaleConverter
发布运行应用程序,在浏览器中键入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)”的方法。下面简单地介绍一下函数的参数:
context——用于获取当前的ActionContext
value——需要转换的值
toType——需要转换成的目标类型
实现转换器,我们需要通过配置告诉Struts 2.0。我们可以通过以下两种方法做到这点:
配置全局的类型转换器,也即是上例的做法——在源代码文件夹下,新建一个名为“xwork-conversion.properties”的配置文件,并在文件中加入“待转换的类型的全名(包括包路径和类名)=转换器类的全名”对;
应用于某个特定类的类型转换器,做法为在该类的包中添加一个格式为“类名-conversion.properties”的配置文件,并在文件中加入“待转换的属性的名字=转换器类的全名”对。上面的例子也可以这样配置——在源代码文件夹的tutorial包下新建名为“HelloWorld-conversion.properties”文件,并在其中加入“loc=tutorial.LocaleConverter”。
在继承DefaultTypeConverter时,如果是要将value转换成其它非字符串类型时,要记住value是String[]类型,而不是String类型。它是通过request.getParameterValues(String arg)来获得的,所以不要试图将其强行转换为String类型。 |
已有的转换器
对于一此经常用到的转换器,如日期、整数或浮点数等类型,Struts 2.0已经为您实现了。下面列出已经实现的转换器。预定义类型,例如int、boolean、double等;
日期类型, 使用当前区域(Locale)的短格式转换,即DateFormat.getInstance(DateFormat.SHORT);
集合(Collection)类型, 将request.getParameterValues(String arg)返回的字符串数据与java.util.Collection转换;
集合(Set)类型, 与List的转换相似,去掉相同的值;
数组(Array)类型, 将字符串数组的每一个元素转换成特定的类型,并组成一个数组。
对于已有的转换器,大家不必再去重新发明轮子。Struts在遇到这些类型时,会自动去调用相应的转换器。
批量封装对象(Bean)
不知道大家是否遇过这种情况,在一个页面里同时提交几个对象。例如,在发布产品的页面,同时发布几个产品。我在之前一个项目就遇到过这种需求,当时用的是Struts 1.x。那是一个痛苦的经历,我在Google搜了很久都没有理想的结果。幸运的是,在Struts 2.0中这种痛苦将一去不复返。下面我就演示一下如何实现这个需求。首先,在源代码文件夹下的tutorial包中新建Product.java文件,内容如下:
package
tutorial;
import
java.util.Date;
public
class
Product
{
private
String name;
private
double
price;
private
Date dateOfProduction;
public
Date getDateOfProduction()
{
return
dateOfProduction;
}
public
void
setDateOfProduction(Date dateOfProduction)
{
this
.dateOfProduction
=
dateOfProduction;
}
public
String getName()
{
return
name;
}
public
void
setName(String name)
{
this
.name
=
name;
}
public
double
getPrice()
{
return
price;
}
public
void
setPrice(
double
price)
{
this
.price
=
price;
}
}
然后,在同上的包下添加ProductConfirm.java类,代码如下:
package
tutorial;
import
java.util.List;
import
com.opensymphony.xwork2.ActionSupport;
public
class
ProductConfirm
extends
ActionSupport
{
public
List
<
Product
>
products;
public
List
<
Product
>
getProducts()
{
return
products;
}
public
void
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;
}
}
接看,在同上的包中加入ProductConfirm-conversion.properties,代码如下:
Element_products
=
tutorial.Product
再在struts.xml文件中配置ProductConfirm Action,代码片段如下:
<
action
name
="ProductConfirm"
class
="tutorial.ProductConfirm"
>
<
result
>
/ShowProducts.jsp
</
result
>
</
action
>
在WEB文件夹下新建AddProducts.jsp,内容如下:
<%
@ page contentType
=
"
text/html; charset=UTF-8
"
%>
<%
@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
>
在同样的文件夹下创建ShowProducts.jsp,内容如下:
<%
@ page contentType
=
"
text/html; charset=UTF-8
"
%>
<%
@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
>
发布运行应用程序,在浏览器中键入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
21
00
:
00
:
00
CST
2004
Pro Spring |
32.99
| Mon Jan
31
00
:
00
:
00
CST
2005
Core J2EE Patterns: Best Practices and Design Strategies
,
Second Edition |
34.64
| Sat May
10
00
:
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)介绍
》。
转换错误处理
不知道大家在运行上面的例子时,有没有填错日期或数字情况,又或者您有没有思考过这种情况?如果还没有尝试的朋友可以试一下,在第一行的Price和Date of production中输入英文字母,然后按“Submit”提交。你会看到页面为空白,再看一下服务器的控制台输出,有如下语句: 警告: No result defined for action tutorial.ProductConfirm and result input,它提示我们没有为Action定义输入结果,所以,我们应该在源代码文件夹下的struts.xml中的ProductConfirm Action中加入以下代码:
<
result
name
="input"
>
/AddProducts.jsp
</
result
>
重新加载应用程序,刷新浏览器重新提交请求,这时页面返回AddProducts.jsp,格式错误的输入框的值被保留,如下图6所示:
图6 没有提示的错返回页面
当然,我们还可以在页面上加上错误提示信息,通过在AddProducts.jsp的“<body>”后,加入下面代码可以实现:
<
div
style
="color:red"
>
<
s:fielderror
/>
</
div
>
刷新浏览器,重新提交请求,出现如图7所示页面:
图7 带提示的错返回页面
以上的功能的都是通过Struts 2.0里的一个名为conversionError的拦截器(interceptor)工作,它被注册到默认拦截器栈(default interceptor stack)中。Struts 2.0在转换出错后,会将错误放到ActionContext中,在conversionError的作用是将这些错误封装为对应的项错误(field error),因此我们可以通过<s:fielderror />来将其在页面上显示出来。另外,大家看第二和第三行的Price都被赋为0.0的值,而第一行则保留其错误值。这同样是conversionError的功劳——没有出错的行调用的products[index].price(默认值为0.0),而出错的行则会被赋为页面所提交的错误值,这样可以提供更好的用户体验。
总结
Struts 2.0的转换器简化的WEB应用程序的模型,为我们的编程带来极大的方便。文章来源:http://www.blogjava.net/max/archive/2006/11/07/79602.html
相关文章推荐
- 转换器(Converter)Struts 2.0中的魔术师
- 转换器(Converter)——Struts 2.0中的魔术师(Struts2中数据批量保存)[转别人的]
- 转换器(Converter)——Struts 2.0中的魔术师
- 转换器(Converter)——Struts 2.0中的魔术师
- 转换器(Converter)——Struts 2.0中的魔术师
- 转换器(Converter)——Struts 2.0中的魔术师(Struts 2.0系列之五)
- 转换器(Converter)——Struts 2.0中的魔术师
- 转换器(Converter)——Struts 2.0中的魔术师
- 转换器(Converter)——Struts 2.0中的魔术师
- 转换器(Converter)——Struts 2.0中的魔术师
- 转换器(Converter)——Struts 2.0中的魔术师
- 自定义struts2.0中的转换器(converter)
- 基于StrutsTypeConverter的数组属性的类型转换器
- Struts ActionForm中自定义转换器Converter(简单易懂)
- 转换器--Struts 2_0中的魔术师
- struts2.0配置文件、常量配置详解
- Struts2.0配置文件的加载分析
- Struts2.0与DWR2.0的整和
- struts2.0+spring intercepter 不能注入属性
- struts1.0于struts2.0的比较