一、什么是类型转换
Struts2是一个MVC框架,它需要负责解析HTTP请求参数,而HTTP请求参数全部为字符串,Java为强类型语言,所以必须根据HTTP请求参数将其中的数据转换为需要的数据类型。这个过程便是类型转换。
二、Struts2的类型转换
Struts2中常用的类型转换可以使用OGNL表达式完成,即使用Struts2内建的类型转换器,部分自定义的数据类型有特殊的转换需求时,也可以通过是实现Struts2给出的接口实现自定义的类型转换器。另外Struts2的类型转换会通过conversionError(在默认拦截器栈中)拦截器对出现的转换错误进行自动拦截处理。
三、Struts2内建的类型转换器
- boolean和Boolean:字符串true会转换为布尔类型值true
- char和Character:字符串转字符
- int和Integer:字符串转整型类型
- long和Long:字符串转长整型
- float和Float:字符串转单精度浮点型
- double和Double:字符串转双精度浮点型
- Date:字符串转日期类型,需要字符串满足一定的格式
- 数组:多个input表单提交给同一个Action的属性,就会构成一个数组传入到该属性中
- 集合:默认情况下,假定集合元素类型为String,并创建一个新的ArrayList封装所以的字符串
以上的类型转换由Struts2自动完成
四、基于OGNL的类型转换
通过ONGL实现字符串与基本类型的自动转换,另外还可以通过OGNL进行简单的复合类型转换。下面这个Action中的user为一个普通的JavaBean,拥有两个属性 userName和passWord。
public class LoginAction extends ActionSupport {
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
...
}
在表单页使用OGNL提交与获取方式:
<!-- 提交 -->
<s:form action="login">
<s:textfield name="user.userName"></s:textfield>
<s:textfield name="user.passWord"></s:textfield>
</s:form>
<!-- 获取 -->
<s:property value="user.userName"></s:property>
<s:property value="user.passWord"></s:property>
但是User必须用于无参构造器,以及各个属性的setter方法,当然还应该在LoginAction为user属性设置setter方法。
另外如果通过OGNL进行Map、List集合的类型转换:
<!-- Map mapName['keyName'].paramName -->
<!-- 这里Map的key为Stirng类型,value为User类型 -->
<s:textfield name="users['one'].username" label="用户名"></s:textfield>
<s:textfield name="users['one'].password" label="密码"></s:textfield>
<!-- List listName[index].paramName -->
<!-- 这里List存放的是User类型,index为索引 -->
<s:textfield name="users[0].username" label="1用户名"></s:textfield>
<s:textfield name="users[0].password" label="1密码"></s:textfield>
<s:textfield name="users[1].username" label="2用户名"></s:textfield>
<s:textfield name="users[1].password" label="2密码"></s:textfield>
五、指定集合元素类型
集合使用泛型指定集合元素类型时Struts2通过反射创建对应的对象,当不使用泛型时Struts2便不知道转换为什么类型的对象了,此时可以通过Struts2的局部类型转换文件(ActionName-conversion.propertiers)指定元素类型。
如LoginAction.java 中不指定List类型
public class LoginAction extends ActionSupport {
private List users;
public List getUsers() {
return users;
}
public void setUsers(List users) {
this.users = users;
}
...
}
可以在LoginAction同一把目录下增加一个名为 ‘LoginAction-conversion.propertiers’ 的properties文件:
#Element_List属性名=类型
Element_users=com.ren.bean.User
如果为Map指定key-value:
#key_属性集合名=类型
#Element_属性集合名=类型
Key_users=String
Element_users=User
六、自定义类型转换器
大部分的类型转换可以通过OGNL机制自动实现 ,但是有时需要将一个特殊的字符串转换为一个数据对象,如将字符串“ren,123”转换为一个userName=“ren”;passWord=“123”;的User对象,这时你可以通过实现 TypeConverter 接口或扩展该接口的实现类 DefaultTypeConverter 来实现自定义类型转化器,另外你可以扩展由Struts2提供的抽象类 StrutsTypeConverter 来实现。下面介绍两种方法的区别。
1、DefaultTypeConverter 类:需要自己根据 一个class类型的参数 toType 来判断要进行 String->User 还是 User->String,所以toType即要转换的目标类型。
public class UserConverter extends DefaultTypeConverter {
@Override
public Object convertValue(Map context, Object value, Class toType) {
// context : 类型转换环境上下文
// value : 需要转换的参数
//toType : 转换后的目标类型
if (toType == User.class) {
String[] params = (String[])value;
User user = new User();
String[] userValues = params[0].split(",");
user.setUsername(userValues[0]);
user.setPassword(userValues[1]);
return user;
} else if (toType == String.class) {
User user = (User)value;
return "<" + user.getUsername() + "," + user.getPassword() + ">";
}
return null;
}
}
2、StrutsTypeConverter 类:该类是DefaultTypeConverter 的子类,但是他给出了两个方法分别代表两个不同的类型转换方向,即 String->User: covertFromString() 与 User->String: convertToString()
public class UserStrutsConverter extends StrutsTypeConverter {
@Override
public Object convertFromString(Map map, String[] strings, Class aClass) {
User user = new User();
String[] userValues = strings[0].split(",");
user.setUsername(userValues[0]);
user.setPassword(userValues[1]);
return user;
}
@Override
public String convertToString(Map map, Object o) {
User user = (User)o;
return "<" + user.getUsername() + "," + user.getPassword() + ">";
}
}
七、注册类型转换器
自定义的类型转换器通过注册起效,注册的方法有三种:
- 局部类型转换器 :仅对某个Action起作用
- 全局类型转换器:对所有Action的 某个类型起作用
- 使用JDK1.5的注解注册
1、局部类型转换器:在与Action相同目录下的 AvtionName-conversion.properties (ActionName为Action名)文件中指定,仅对本Action中的某个属性起作用,仅执行一次。
#User 的自定义转换器
#属性名=住转换器
user=com.ren.converter.UserConverter
2、全局类型转换器:在 WEB-INF/classes (idea放在src文件夹下编译项目时会自动放到classes目录下) 文件夹中的 xwork-conversion.properties (固定名字) 文件中指定,对整个web项目中的某个类型起作用,有多少个执行多少次。如:List<User> 长度为n,便执行n次。
#全局转换器配置
#类型名=转换器
com.ren.bean.User=com.ren.converter.UserConverter
一定注意具备类型转换器只针对某个Action中的某个属性起作用,哪怕是一个集合也只会执行一次转换,全局类型转换器则会执行多次,只要需要转换时。
八 、类型转换中的错误处理
上面的自定义类型转换器 UserConverter 可以将一个像 “ren,123” 的字符串转换为一个 User 实例,用户如果输入已 ‘,’隔开的字符串时它可以成功的完成转换任务,但是当用户输入不带 ‘,’ 的字符串时它便 不能起作用了,执行便会出错,或者应该输入数字时用户却输入了字符这同样会出错。值得开心的是Struts2有能力自动的对类型转换中出现的错误进行收集、封装、处理,你不需要在程序中进行任何有关转换错误的处理。
Struts通过 默认拦截器栈中的 conversionError 拦截器进行将出现的错误信息封装成“表单域错误”(FieldError),并将它放入ActionContext 中。但是前提是你的 Action 必须继承 ActionSupport,它将异常信息封装抛出(throws)由拦截器进行处理。(你可以在Struts2默认配置文件 struts-default.xml 中查看默认拦截器栈的定义)
import com.opensymphony.xwork2.ActionSupport;
public class LoginSetAction extends ActionSupport {
...
}
另外出现错误后返回 “input” 所以你需要在action中配置一个 name 属性为 input 的 result。
<action name="login" class="com.ren.action.LoginAction">
<result name="success">/WEB-INF/content/login_success.jsp</result>
<!--转换失败后返回input-->
<result name="input">/WEB-INF/content/input.jsp</result>
<result name="error">/WEB-INF/content/error.jsp</result>
</action>
你可以在 input 视图中使用 <s:fielderror> 标签来显示错误信息。它会输出类似 “Invalid field value for field xxx” 的错误信息,xxx为 Action 名字。如果希望打印中文提示信息,在国际化文件中增加如下语句:
#{0} 为出错Action名
xwork.default.invalid.fieldvalue={0}你的提示信息