Web应用程序的交互都是建立在HTTP之上的,互相传递的都是字符串。也就是说服务器接收到的来自用户的数据只能是字符串或者是字符数组,而在Web应用的对象中,往往使用了多种不同的类型,如整数(int)、浮点数(float)、日期(Date)或者是自定义数据类型等。因此在服务器端必须将字符串转换成合适的数据类型。
Struts2框架中为我们提供了一些简单类型的转换器,比如转换为int、float等简单数据类型是不需要我们自己定义转换器去转换的,struts2内部本身就为我们提供了转换的方法,但像一些复杂的类型和我们自定义的数据类型还是需要我们自己去写转换器去转换的。在转换工程中,如果在类型转换中出现异常,类型转换器开发者无需关心异常处理逻辑,Struts2的conversionError拦截器会自动处理该异常,并且提示在页面上生成提示信息。
下面我们就一步步的实现和注册一个我们自己的转换器,也就是自定义类型转换器的几个步骤:
一:自定义类型转换器
实现自定义类型转换器我们一般有两种方式:
1.实现OGNL提供的TypeConvert接口以及实现了TypeConvert接口的DefaultTypeConvert类来实现自定义的类型转换器。我们来看一下DefaultTypeConvert类的源码:
- publicclass DefaultTypeConverter implements TypeConverter{
- public DefaultTypeConverter(){
- super();
- }
- /**
- * @param context:类型转换的上下文
- * @param value:需要转换的参数
- * @param toType:转换后的目的类型
- */
- public Object convertValue(Map context,
- Object value,
- Class toType)
- {
- return OgnlOps.convertValue(value, toType);
- }
- public Object convertValue(Map context, Object target,
- Member member, String propertyName,
- Object value, Class toType)
- {
- return convertValue(context, value, toType);
- }
- }
convertValue方法的作用:
该方法负责完成类型的双向转换,为了实现双向转换,我们通过判断toType的类型即可判断转换的方向。toType类型是需要转换的目标类型,如:当toType类型是User类型时,表明需要将字符串转换成User实例;当toType类型是String类型时,表明需要把User实例转换成字符串类型。通过toType类型判断了类型转换的方向后,我们就可以分别实现两个方向的转换逻辑了。实现类型转换器的关键就是实现conertValue方法,该方法有三个参数:
第一个参数 context:类型转换的上下文
第二个参数 value:需要转换的参数
第三个参数 toType:转换后的目的类型
2. 基于Struts2的类型转换器
Struts 2提供了一个StrutsTypeConverter的抽象类,这个抽象类是DefaultTypeConverter类的子类。开发时可以直接继承这个类来进行转换器的构建。通过继承该类来构建类型转换器,可以不用对转换的类型进行判断(和第一种方式的区别),下面我们来看一下StrutsTypeConverter类的源码:
- publicabstractclass StrutsTypeConverter extends DefaultTypeConverter {
- //重写DefaultTypeConverter类的convertValue方法
- public Object convertValue(Map context, Object o, Class toClass) {
- //如果需要把复合类型转换成字符串类型
- if (toClass.equals(String.class)) {
- return convertToString(context, o);
- }
- //如果需要把字符串转换成符合类型
- elseif (o instanceof String[]) {
- return convertFromString(context, (String[]) o, toClass);
- }
- //如果需要把字符串转换成符合类型
- elseif (o instanceof String) {
- return convertFromString(
- context, new String[]{(String) o}, toClass);
- } else {
- return performFallbackConversion(context, o, toClass);
- }
- }
- protected Object performFallbackConversion(Map context,
- Object o, Class toClass) {
- returnsuper.convertValue(context, o, toClass);
- }
- publicabstract Object convertFromString(Map context,
- String[] values, Class toClass);
- publicabstract String convertToString(Map context, Object o);
- }
该类已经实现了DefaultTypeConverter的convertValue方法。实现该方法时,它将两个不同转换方向替换成不同方法——当需要把字符串转换成复合类型时,调用convertFromString抽象方法;当需要把复合类型转换成字符串时,调用convertToString抽象方法,下图展示了其对应关系:
二.注册自定义类型转换器:
实现了自定义的类型转换器之后,将该类型转换器注册在Web应用中,Struts2框架才可以正常使用该类型转换器,类型转换器的注册分为两种,
1.注册局部类型转换器。
局部类型转换器仅仅对某个Action起作用。局部类型转换器非常简单,只需要在相应的Action目录下新建一个资源文件。该资源文件名格式如下。ActionName-conversion.properties。其中ActionName表示需要进行转换的Action的类名,“-conversion.properties”字符串则是固定格式的。该文件也是一个典型Properties文件,文件由键值对组成:propertyName = 类型转换器类
如:name=util.NameConvert
name:表示要进行转换的属性
util.NameConvert:表示要进行转换的自定义类型转换器。
注意:该属性文件应该与ActionName.class放在相同位置。
2.注册全局类型转换器。对所有Action的特定类型的属性都会生效。
全局类型转换器,必须提供一个xwork-conversion.properties文件。文件必须保存在classes目录下。该资源文件名格式如下:
复合类型=对应的类型转换器
复合类型:指定需要完成类型转换的复合类
对应的类型转换器:指定所指定类型转换的转换器。
如:注册User类的全局类型转换器为:UserConverter
cn.wjz.bean.User = cn.wjz.util.UserConverter
注意:如果局部类型转换和全局类型转换同时存在的话,局部类型转换具有较高的优先级,也就是以局部类型转换器为主。
三.集合类型的类型转换
对于List元素来说,内容如: Element_attributeName=typeName;对于Map元素来说,
(1)如果表示key的类型,则:Key_attributeName=typeName;
(2)如果表示value的类型,则为:Element_attributeName=typeName;
比如,此处没有使用泛型,而是使用了局部类型转换文件:
- Conversion02Action.java
- public class Conversion02Action extends ActionSupport {
- private List lists;
- private Map maps;
- public String execute()throws Exception{
- System.out.println(((Person)lists.get(0)).getGender());
- System.out.println(((Person)lists.get(0)).getSalary());
- System.out.println(((Person)maps.get("one")).getGender());
- System.out.println(((Person)maps.get("one")).getSalary());
- return SUCCESS;
- }
- public List getLists() {
- return lists;
- }
- public void setLists(List lists) {
- this.lists = lists;
- }
- public Map getMaps() {
- return maps;
- }
- public void setMaps(Map maps) {
- this.maps = maps;
- }
- }
- Element_lists=org.person.Person
- Key_maps=java.lang.String
- Element_maps=org.person.Person
- <s:fielderror></s:fielderror>
- <s:form action="conversion02" >
- <s:textfield label="list1.salary" name="lists[0].salary"></s:textfield>
- <s:textfield label="list1.gender" name="lists[0].gender"></s:textfield>
- <s:textfield label="map1.gender" name="maps['one'].gender"></s:textfield>
- <s:textfield label="map1.salary" name="maps['one'].salary"></s:textfield>
- <s:submit value="提交"></s:submit>
- </s:form>
四.Struts 2内建的类型转换器 :
Sturts 2为常用的数据类型提供了内建的类型转换器,所以根本不用自定义转换器。对于内建的转换器,Struts在遇到这些类型时,会自动去调用相应的转换器进行类型转换。
Struts 2全部的内建转换器:
·基本数据类型以及其封装类。包括:boolean和Boolean、char和Character、int和Integer、long和Integer、float和Float、double和Double。完成字符串和基本数据类型或其封装类之间的转换。
·日期类型。使用当前区域的短格式转换,即DateFormat.getInstance(DateFormat.SHORT)完成字符串和日期类型之间的转换。
·集合(Collection)类型。将request.getParameterValues(String arg)返回的字符串数据与.util.Collection转换。集合元素为String类型。
·集合(Set)类型。与Collection的转换相似,只是去掉了相同的值。集合元素为String类型。
·数组类型。将request.getParameterValues(String arg)返回的字符串数组中的每个字符串值取出组成一个数组。数组元素为String类型。
注意:Struts 2提供的全部内建转换器都是双向的,也就是说从用户输入页到服务器端时会将字符串类型转换成相应的数据类型。在显示输出时,又会将相应的数据类型转换成字符串类型来显
数组类型的转换器。这个转换器非常有用,比如多个表单元素的name属性相同,那么提交的参数就不再是字符串而是一个字符串数组。通过Sturts 2提供的数组类型的转换器就能很方便的将多个相同name属性的表单元素的值封装到Action中的一个数组中。
五.类型转换中错误处理:
1.类型转换的错误处理流程:
Struts2提供了一个名为conversionError的拦截器,这个拦截器被注册在默认的拦截器栈中,在Struts-default.xml中的配置信息:
- <interceptor-stack name="defaultStack">
- .. .. ..
- <!—- 处理类型转换错误的拦截器 -->
- <interceptor-ref name="conversionError"/>
- <!—- 处理数据校验的拦截器 -->
- <interceptor-ref name="validation">
- <param name="excludeMethods">input,back,cancel,browse</param>
- </interceptor-ref>
- <interceptor-ref name="workflow">
- <param name="excludeMethods">input,back,cancel,browse</param>
- </interceptor-ref>
- </interceptor-stack>
Struts2的错误处理流程:
2、错误信息的友好显示
在进行类型转换中,如果出现错误将会提示错误信息。Struts 2默认提供了错误信息提示,但是这些错误信息提示不够友好,下面将介绍如何自定义错误信息来取代Struts 2的默认错误信息。
·定义全局类型转换错误处理信息:
在应用的国际化资源文件中增加如下的信息:
xwork.default.invalid.fieldvalue = key
key的值就是用户希望在页面中显示的提示信息。 例如:
#改变默认的类型转换失败后的提示信息 xwork.default.invalid.fieldvalue = {0}字段类型转换失败!! 因为包含非西欧字符,因此使用 native2ascii 命令处理 xwork.default.invalid.fieldvalue = {0}\u5b57\u6bb5\u7c7b\u578b\u8f6c\u6362\u5931\u8d25\uff01\uff01 |
·定义局部类型转换错误处理信息:
在某些时候可能还需要对特定的字段指定特别的提示信息,此时可以提供该Action的局部资源文件,文件名:ActionName.properties , 在文件中增加如下一项:
invalid.fieldvalue.属性名 =提示信息
例如:
#改变Action中birth属性类型转换错误后的提示信息 invalid.fieldvalue.birth = 生日信息必须满足yyyy-MM-DD格式 使用 native2ascii 命令处理 invalid.fieldvalue.birth = \u751f\u65e5\u4fe1\u606f\u5fc5\u987b\u6ee1 \u8db3yyyy-MM-DD\u683c\u5f0f
|
六.类型转换的流程
1、用户进行请求,根据请求名在struts.xml中寻找Action
2、在Action中,根据请求域中的名字去寻找对应的set方法。找到后在赋值之前会检查这个属性有没有自定义的类型转换。没有的话,按照默认进行转换;如果某个属性已经定义好了类型转换,则会去检查在Action同一目录下的action文件名-conversion.properties文件。
3、从文件中找到要转换的属性及其转换类。
4、然后进入转换类中,在此类中判断转换的方向。我们是先从用户请求开始的,所以这时先进入从字符串到类的转换。返回转换后的对象。流程返回Action。
5、将返回的对象赋值给Action中的属性,执行Action中的execute()
6、执行完execute()方法,根据struts.xml的配置转向页面
7、在jsp中显示内容时,根据页面中的属性名去调用相应的get方法,以便输出
8、在调用get方法之前,会检查有没有此属性的自定义类型转换。如果有,再次跳转到转换类当中。
9、在转换类中再次判断转换方向,进入由类到字符串的转换,完成转换后返回字符串。
10、将返回的值直接带出到要展示的页面当中去展示。