我们知道,从前台表单传递到后台的Model对象的属性上时必定要涉及到类型转换的问题,前台的表单当中的参数内容只能是一个类型,就是String类型或者String数组,而后台的属性类型却可以是多种多样的,也就是说,后台对象的属性想要接收前台传递过来的字符串的请求参数,就必须将String类型转换成属性的类型才可以接收。
1.Struts2的自动类型转换
在Struts2中,它是通过一系列的拦截器来完成主要功能的,而将请求参数赋值到值栈对象属性上是ParamsInterceptor拦截器来完成的,在赋值期间,这个ParamInterceptor拦截器可以自动的将字符串类型的请求参数转换成Java当中8个基本数据类型的对象属性。
但是当对象属性不是基本数据类型时,就会出现转换出错的情况,若此时这个Action类没有继承validationAware接口,Struts在遇到类型转换错误时仍会继续调用其Action方法,就好像什么都没发生一样。若此时Action类实现了validationAware接口(ActionSupport类),那么当类型转换发生错误时,Struts2就不会调用Action的方法,它将检查相关action元素节点是否包含一个name=input的result响应页面,若有,Struts2将把控制权交给这个result元素,并在其响应页面自动显示错误消息,若无对应的name=input的result结果,Struts将抛出一个异常。
2.Struts2的错误页面显示
当存在name=input的result的响应页面时,如何在响应页面覆盖默认的错误消息?
在相应的Action下添加一个Action.properties属性文件,并且在其中添加invalid.fieldvalue.'fieldname'(相应的属性名)。
当JSP的Struts2的表单标签主题是simple时,响应页面不会自动显示错误消息,此时需要我们自己定制显示错误消息?
当Action实现ValidationAware接口之后,这个Action对象会有errors和fielderrors属性,当出现类型转换错误时,Struts2的conversionerror拦截器会将错误信息放到Action的对象中的这两个属性中,从而放入值栈栈顶。这个fielderror属性的类型时Map<String,List<String>>,可用EL或者OGNL来显示错误消息。${fieldEooros.age}。也可以用Struts2标签来显示<s:fielderror fieldName="">,fieldName属性来显示指定字段的错误消息。需要注意的是,在simple主题下显示错误消息时,格式中会有span/ul/ui标签,显得格格不入,我们该如何将这些默认的标签去掉呢?一是通过CSS来修改这些标签,二是通过覆盖Struts2默认的模板来修改,我们在src下创建一个template.simple的包,然后创建一个fielderror.ftl文件,将原生的fielderror的内容复制进来并且将里边的ul/ui/span内容给删除掉就可以了。
3.Struts2的自定义类型转换
前边讲到Struts2可以将String类型或者String数组类型自动转换成Model对象属性的基本数据类型,但是如果Model中定义的属性是引用数据类型时会报错,那么该如何正确转换而不报类型转换异常呢?这就需要我们来自定义数据转换类型。
①定义转换器的类
定义一个新的Java类并实现StrutsTypeConverter这个接口,然后需要重写这个接口的“一来一往”的两个转换方法,即将表单请求参数转换为想要的属性类型(甚至可以声明想要的类型的格式,如:yyyy-MM-dd)和将属性的类型转换为String类型。
需要说明的是,我们希望将String类型转换成我们想要的类型,甚至可以指定目标类型的格式,但是不希望在Conversion类中直接写明,而是希望从web.xml初始化参数中进行读取,就是将这种格式变成可配置的内容来进行读取。
public class TestConvertor extends StrutsTypeConverter {
private DateFormat dateformat ;
public TestConvertor() {
System.out.println("TestConvertor");
}
public DateFormat getDateFormat(){
ServletContext servletContext = ServletActionContext.getServletContext();
String pattern = servletContext.getInitParameter("pattern");
dateformat = new SimpleDateFormat(pattern);
return dateformat;
}
@Override
public Object convertFromString(Map arg0, String[] arg1, Class arg2) {
if (arg2 == Date.class) {
if (arg1!=null & arg1.length>0) {
String value = arg1[0];
try {
return getDateFormat().parseObject(value);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
return null;
}
@Override
public String convertToString(Map arg0, Object arg1) {
if (arg1 instanceof Date) {
Date date = (Date)arg1;
return getDateFormat().format(date);
}
return null;
}
}
web.xml
<context-param>
<param-name>pattern</param-name>
<param-value>yy-MM-dd hh:mm:ss</param-value>
</context-param>
② Struts2可以按照针对对象的不同定义两种配置自定义转换类型的方式。
a 基于某个Model对象的字段进行配置
在字段所在类的同名包下定义一个ModelClassMame-converision.properties文件,并在文件当中声明一个键值对:fieldName=类型类型转换器的全类名
b 针对String类型要转换成的目标对象属性的类型进行配置
这种配置方式会使用于所有的Model中的类型转换,所以我们要将配置文件放在src下,新建一个xwork.conversion.properties文件,并在其中声明一个键值对:目标转换的类型=转换器的全类名。
以上两种方法具有明显的区别,我们建议使用全局的基于类型的配置来使用转换器。
需要注意的是:两种配置加载的转换器的时机不同,基于属性的配置是当提交表单时才会创建转换类的实例并调用转换方法,并且只会创建一次对象,是单实例安全的,只会再转换都只会调用这个对象的方法而不会创建对象;而基于类型配置的配置文件,是当服务器加载Struts2框架时会调用转换类的构造器创建两次对象,不是单实例的。二者对象创建的时机不同所以第二种就无法在构造器内部使用ServletActionContext对象,因为还没有发出请求,需要再写一个方法来通过ServletActionContext对象获取web.xml中的初始化参数。
4. 类型转换器与复杂属性或者集合协同使用
全局类型转换器依然可以正常工作。