Web应用程序的交互都是建立在HTTP之上的,Http请求参数都是字符串类型,服务器接收到的来自用户的数据只能是字符串或者是字符数组,而Java是强类型的语言,Web应用的对象可能使用如整数(int)、浮点数(float)、日期(Date)或者自定义数据类型等,为此要求在服务端把字符串参数转换为相应的数据类型。
从一个 HTML 表单到一个 Action 对象, 类型转换是从字符串到相应类型,表现层数据的流向和所需的类型转换如下图,
在 struts2 中, 把请求参数映射到 action 属性的工作由 Parameters 拦截器负责, 它是默认的 defaultStack 拦截器中的一员. Parameters 拦截器可以自动完成字符串和基本数据类型之间转换,比如转换为int、float等简单数据类型是不需要我们自己定义转换器去转换的,struts2内部本身就为我们提供了转换的方法,但像一些复杂的类型和我们自定义的数据类型还是需要我们自己去写转换器去转换的。struts2提供了很好的扩展性,开发者可以方便地开发自定义类型转换器,完成字符串和自定义复合类型之间的转换(例如,完成字符串到Person实例的转换)。
如果类型转换过程中发生未知异常类型转换器开发者不用关心异常处理逻辑,struts2的conversionError会自动处理该异常,并且在页面上生成提示信息。
大部分常用类型不需要开发者理会,它们通过struts2内建的类型转换器完成,struts内建类型转换器已经能够完成字符串类型与这些类型之间相互的类型转换:boolean和Boolean,char和Character,int和Integer,long和Long,float和Float,double和Double,Date,数组,集合等。
开发者要关注的是将字符串转换成复合类型,可以通过实现OGNL提供的TypeConvert接口,或者实现TypeConvert接口的DefaultTypeConvert类来,或者借助OGNL表达式的支持(以简单方式),实现自定义的类型转换器。先来看一个Action类的代码。
public class User{
private String name;
private String pass;
// name的setter和getter方法
public void setName(String name){
this.name = name;
}
public String getName(){
return this.name;
}
// pass的setter和getter方法
public void setPass(String pass){
this.pass = pass;
}
public String getPass(){
return this.pass;
}
}
Action里包含了一个User类型的属性,这个属性需要进行类型转换,struts2框架接受到HTTP请求参数后,要将这些请求参数封装成User对象。
struts2提供的OGNL表达式允许开发者无须任何特殊处理,只需要在定义表单域时使用OGNL表达式来定义表单域的name属性,jsp页面代码如下。
<s:form action="login">
<!-- 该表单域封装的请求参数名为user.name -->
<s:textfield name="user.name" label="用户名"/>
<!-- 该表单域封装的请求参数名为user.pass -->
<s:textfield name="user.pass" label="密码"/>
<tr>
<td colspan="2"><s:submit value="转换" theme="simple"/>
<s:reset value="重填" theme="simple"/></td>
</tr>
</s:form>
上面代码定义了两行单行文本框,对应两个请求参数,表单请求参数名并不是普通参数,而是user.name,user.pass的形式,这就是OGNL表达式的形式,struts2会把user.name参数的值复制给Action实例的user属性的name属性,并将user.pass参数的值赋值给Action实例的user属性的pass属性。
struts2通过这种方式可以将普通请求参数转换成复合类型对象,但在使用时有几点需要注意。
1. 由于struts2将通过反射来创建一个复合类(User类)的实例,因此系统必须为该复合类提供无参构造器。
2. 如果希望使用user.name请求参数的形式为Action实例的user属性的name属性赋值,则必须为user属性对应的复合类(User类)提供setName()方法,因为struts2是通过调用该方法来为该属性赋值的。Action类中还应该包含getUser()方法。
极端情况下,可以直接产生Collection,或者Map实例。看如下的Action类片段。
public class LoginAction extends ActionSupport
{
// Action类里包含一个Map类型的成员变量
// Map的value类型为User类型
private Map<String , User> users;
// users的setter和getter方法
public void setUsers(Map<String , User> users)
{
this.users = users;
}
public Map<String , User> getUsers()
{
return this.users;
}
public String execute() throws Exception
{
// 在控制台输出Struts 2封装产生的Map对象
System.out.println(getUsers());
// 根据Map集合中key为one的User实例来决定控制逻辑
if (getUsers().get("one").getName().equals("crazyit.org")
&& getUsers().get("one").getPass().equals("leegang") )
{
addActionMessage("登录成功!");
return SUCCESS;
}
addActionMessage("登录失败!!");
return ERROR;
}
}
这里Action类定义了一个users属性,该属性的类型是Map
<s:form action="login">
<s:textfield name="users[‘one‘].name" label="第one个用户名"/>
<s:textfield name="users[‘one‘].pass" label="第one个密码"/>
<s:textfield name="users[‘two‘].name" label="第two个用户名"/>
<s:textfield name="users[‘two‘].pass" label="第two个密码"/>
<tr>
<td colspan="2"><s:submit value="转换" theme="simple"/>
<s:reset value="重填" theme="simple"/></td>
</tr>
</s:form>
将表单域的name属性设置为“Action属性名[‘key值’].属性名”的形式,其中“Action属性名”是Action类里包含的Map类型属性,后一个属性名则是Map对象里复合类型对象的属性名。通过这种方式,struts2可以将HTTP请求参数转换成Map属性。
如果要访问Action的Map类型,也可以用OGNL表达式,代码如下。
key为one的用户名为:<s:property value="users[‘one‘].name"/><br/>
key为one的密码为:<s:property value="users[‘one‘].pass"/><br/>
key为two的用户名为:<s:property value="users[‘two‘].name"/><br/>
key为two的密码为:<s:property value="users[‘two‘].name"/><br/>
如果把LoginAction中的users属性改为List,即如果需要struts2将用户请求参数封装为List类型属性,一样可以利用OGNL表达式做到,只要通过索引来指定要将请求参数转换成List的哪个元素,下面JSP页面里的表单元素的name属性可实现将HTTP请求参数转换成List类型。
<s:form action="login">
<s:textfield name="users[0].name" label="第一个用户名"/>
<s:textfield name="users[0].pass" label="第一个密码"/>
<s:textfield name="users[1].name" label="第二个用户名"/>
<s:textfield name="users[1].pass" label="第二个密码"/>
<tr>
<td colspan="2"><s:submit value="转换" theme="simple"/>
<s:reset value="重填" theme="simple"/></td>
</tr>
</s:form>
上面代码中定义表单域时指定第一个文本域的name为users[0].name,struts2将会把该文本域所代表的请求参数转换成users集合第一个元素的name属性。
如果想输出Action中List属性里各集合元素的属性值,则可通过在集合属性后增加索引来访问,代码如下。
第一个User实例的用户名为:<s:property value="users[0].name"/><br/>
第一个User实例的密码为:<s:property value="users[0].pass"/><br/>
第二个User实例的用户名为:<s:property value="users[1].name"/><br/>
第二个User实例的密码为:<s:property value="users[1].name"/><br/>