strut2(xwork)中的类型转换策略

来源:互联网 发布:程序员必备生活用品 编辑:程序博客网 时间:2024/06/06 02:01
 

 类型转换是struts2的一个比较重要的特点(当然这个特点也是来源的xwork的),它可以将前台字符型数据转换成后台的需要的各种类型的数据,而且它是一个可以定制各种符合自己要求的各种转换器,也就是说它的转换能力是无限的,整个转换体系符合框架的一贯特点,框架提供一个基本的扩展体系,同时提供基本的转换能力,而开发者可以在此基础上进行定制转换.有一点还需要说明,转换器这一机制追根溯源它也并非来源于xwork,而是来源于OGNL因为最终的参数设置是需要靠OGNL框架来实现的,但是在xwork中对转换器接口,及规范进行重新抽象,并为OGNL转换器进行了适配工作.所以这里就不谈OGNL的话题了.

xwork的提供的转换接口与默认实现类主要有TypeConverter(最高接口);DefaultTypeConverter(基本实现);XWorkBasicConverter(主力实现);XWorkConverter(管理器);StrutsTypeConverter是struts2在对框架进行包装的时候,自己抽象的一个规范抽象类.TypeConverter接口是xwork中转换器的标识,因此所有转换类都必须实现此接口,DefaultTypeConverter是一个基本实现,其实其它的所有转器都继承自这个类.可以看成中xwork中的最高超类.

从整个框架在struts2中的使用的转换器是XWorkConverter但是XWorkConverter主体作为着眼的是定制转换器(专有转换器与共享转换器),而在大多数情况下这种定制转换器并不是很多,只要没有出现过转换器配置文件或标注型转换器的配置,那么XWorkConverter就不会做什么实际的的工作,它会将转换工作委托给作为默认转换器的XWorkBasicConverter来完成,可以这么说吧XWorkBasicConverter承担struts2 90%以上的转换工作,至于它们共同的超类一般也不会承担什么转换任务,因为它不是着眼于web程序的转换任务,因为它着眼于基础类型之间的转换,可以这样来总结:DefaultTypeConverter是为所有命令框架设置基础类型转换器;XWorkBasicConverter是为web应用程序专门设置的基础转换器,或者说是专门为struts2准备的基础类型转换器;而XWorkConverter是专门为定制转换器所设置管理的程序,与其说它是一个转换器,还不如果说它是一个转换器的容器它的转换能力主要来源于两个,一种来源于外部配置,另一源于委托默认转换器的能力.

通过上面的分析,你可以看出XWorkBasicConverter的地位特别适合作为定制转换器的超类,至于StrutsTypeConverter我认为没有什么作用它虽然它本身根据WEB应用程序的特别将转换工作一分为二,这种思想是好的但是它本身提供的功能太弱了,因为它继承自ognl转换器的基础,它的功能可能连DefaultTypeConverter都不如,如果定制的转换器继承它我们就必须完全由自己来完成转换工作,这样对我们没什么好处;当然我们也不能继承XWorkConverter因为它本身还依赖定制转换器,它就是为定制转换器而设置的,而XWorkBasicConverter就不同了,它是专门为web应用程序转换基础类而设置的,我们可以利用它的很多功能,我们可以先做定制工作,然后将其它工作委托超类来完成,比如下面的一个转换器就是如此:它所做的转换工作比较多,比如它可以将前台的一个序列1,2,3,4,....转换后台的一个整数列表或整数数组.

public class CustomShareConverter extends XWorkBasicConverter {public static final StringSEQUENCE_SEPARATOR= ",";public static final StringMAP_SEPARATOR= ":";private ObjectTypeDeterminerobjectTypeDeterminer;private XWorkConverter xworkConverter;@Inject    public void setObjectTypeDeterminer(ObjectTypeDeterminer det) {        this.objectTypeDeterminer = det;    }        @Inject    public void setXWorkConverter(XWorkConverter conv) {        this.xworkConverter = conv;    }/** * 这里的toType一定是一个在配置文件将此转换器作为它的转换器的类或者是String类 */@SuppressWarnings("unchecked")public Object convertValue(Map context, Object target, Member member, String property, Object value, Class toType) {if (value == null || toType == String.class || ((String[]) value).length != 1) {//注意第一个条比较怪,具体解释是:1.如果为null交给超类;2.如果向前台转,也交给超类;//3.前台向后台转,且value不为null,但是前台传递同一个name多于一个值,则仍然使用超类转换return super.convertValue(context, target, member, property, value, toType);} String strValue = ((String[]) value)[0];//我现存在认为只要前台传值至少会有一个元素,放在这个表达式在这里确认一下strValue="null".equals(strValue)?null:strValue;if (toType == Boolean.class) {if (value == null || !StringUtils.hasText(strValue) || "null".equals(strValue)) {return null;} else if ("true".equals(strValue) || "1".equals(strValue)) {return Boolean.TRUE;} else {//注意:除去true,1,与null全部返回falsereturn Boolean.FALSE;}} else if(Number.class.isAssignableFrom(toType)){if (value == null || !StringUtils.hasText(strValue) || "null".equals(strValue)) {return null;} return super.convertValue(context, target, member, property, value, toType);} else if (toType.isArray() || Collection.class.isAssignableFrom(toType)) {//向台向后台转,并且是单参数(即序列转数组)String sequence = strValue;//因为前台序列向后台转报空指针异常,改变逻辑为:如果前台传递空字符串,则变成空列表处理;而"null"字符串转换为null 还没有改写if (!StringUtils.hasText(sequence)||"null".equals(sequence)) {return null;//对序列情况的处理}//这里重写了拆分逻辑value = new SequenceSplitor(sequence).split(SEQUENCE_SEPARATOR.charAt(0));return super.convertValue(context, target, member, property, value, toType);} else if (Map.class.isAssignableFrom(toType)) {//超类并没有给出对map情况的处理,这里直接将它处理掉,但是map中的成员仍然是依赖超类处理return convertToMap(context, target, member, property, value, toType);}throw new RuntimeException("预期外的类型");}//将一个序列转换成一个Mapprivate Object convertToMap(Map<String, Object> context, Object target, Member member, String property, Object value, Class<?> toType) {String sequence = ((String[]) value)[0];String[] splitArray = new SequenceSplitor(sequence).split(SEQUENCE_SEPARATOR.charAt(0));Class<?> keyClass = String.class;Class<?> valueClass = String.class;if (target != null) {keyClass = this.objectTypeDeterminer.getKeyClass(target.getClass(), property);valueClass = this.objectTypeDeterminer.getElementClass(target.getClass(), property, null);}Map<Object, Object> map = createMap(context, target, member, property, value, toType, splitArray.length);for (int index = 0; index < splitArray.length; index++) {String keyValue = splitArray[index];//这里重写了拆分逻辑String[] keyValuePair = new SequenceSplitor(keyValue).split(MAP_SEPARATOR.charAt(0));//注意这个地方注入xworkConverter来进行转换,主要是怕遗漏Map中的键值有可能需要定制转换器,还有一点需要注意,传值使用字符串数组以模拟前台Object key = xworkConverter.convertValue(context, null, null, null, new String[] { keyValuePair[0] }, keyClass);Object val = xworkConverter.convertValue(context, null, null, null, new String[] { keyValuePair[1] }, valueClass);map.put(key, val);}return map;}private Map<Object, Object> createMap(Map<?, ?> context, Object target, Member member, String property, Object value, Class<?> toType, int size) {Map<Object, Object> map;if (toType == ConcurrentMap.class) {if (size > 0) {map = new ConcurrentHashMap<Object, Object>(size);} else {map = new ConcurrentHashMap<Object, Object>();}} else if (toType == SortedMap.class) {map = new TreeMap<Object, Object>();} else {if (size > 0) {map = new HashMap<Object, Object>(size);} else {map = new HashMap<Object, Object>();}}return map;}}


 

它的基本思想就是将前台传过来的结果进行修正操作,然后再将修正后的结果交给它的父类XWorkBasicConverter进行处理,比如最典型的前台一串字符XWorkBasicConverter是没办法转换成一个List列表的,但是此转换器将它先拆分成一个数组,再将处理生的数组交给XWorkBasicConverter它就可以完成此光荣的任务.定制转换器继承XWorkBasicConverter我认为是一个非常不错的选择,虽然你会再用过程会遇到很多的阻力,比如上面的这个转器应该只能工作于xwork2.0.4以前的板版本中,在xwork2.1以后的板版本中它是不能正常工作的,这并不是说这种想法有问题,而是XWorkBasicConverter在两个版本中发生了变化导致的什么样的变化,这涉及到xwrok容器组件注入的问题,你可以看到上面的转换器使用@Inject标注注入了容器组件ObjectTypeDeterminer ,很不幸的在xwork2.1后的版本中XWorkBasicConverter 类中也需要注入这个对象,而且这个注入的方法的签名也是public void setObjectTypeDeterminer(ObjectTypeDeterminer det) {这会导致,超类XWorkBasicConverter 中的注入对象失败,其实这有两个办法可以解决,一种办法就是你将这个方法签名修改成别的方法签名使它不覆盖超类中的方法签名;还有一个办法就是将此继承模式改成委托模式,例如下面将它改成委托模式:

package com.risen.core.util.base;import java.lang.reflect.Member;import java.util.Collection;import java.util.HashMap;import java.util.Map;import java.util.SortedMap;import java.util.TreeMap;import java.util.concurrent.ConcurrentHashMap;import java.util.concurrent.ConcurrentMap;import org.springframework.util.StringUtils;import com.opensymphony.xwork2.conversion.ObjectTypeDeterminer;import com.opensymphony.xwork2.conversion.TypeConverter;import com.opensymphony.xwork2.conversion.impl.XWorkBasicConverter;import com.opensymphony.xwork2.inject.Inject;/** * 主要用于处理一些单值,向容器类型的转换 *  * @version 1.0 * @author: 贺志成 hzc@risencn.com * @datetime: Aug 26, 2011 2:07:13 AM *  */public class CustomShareConverter implements TypeConverter {public static final StringSEQUENCE_SEPARATOR= ",";public static final StringMAP_SEPARATOR= ":";private ObjectTypeDeterminerobjectTypeDeterminer;private XWorkBasicConverter  workBasicConverter;@Injectpublic void setObjectTypeDeterminer(ObjectTypeDeterminer det) {this.objectTypeDeterminer = det;}@Injectpublic void setXWorkConverter(XWorkBasicConverter conv) {this.workBasicConverter = conv;}/** * 这里的toType一定是一个在配置文件将此转换器作为它的转换器的类或者是String类 */@SuppressWarnings("unchecked")public Object convertValue(Map<String, Object> context, Object target, Member member, String property, Object value, Class toType) {if (value == null || toType == String.class || ((String[]) value).length != 1) {//注意第一个条比较怪,具体解释是:1.如果为null交给超类;2.如果向前台转,也交给超类;//3.前台向后台转,且value不为null,但是前台传递同一个name多于一个值,则仍然使用超类转换return workBasicConverter.convertValue(context, target, member, property, value, toType);} String strValue = ((String[]) value)[0];//我现存在认为只要前台传值至少会有一个元素,放在这个表达式在这里确认一下strValue="null".equals(strValue)?null:strValue;if (toType == Boolean.class) {if (!StringUtils.hasText(strValue) || "null".equals(strValue)) {return null;} else if ("true".equals(strValue) || "1".equals(strValue)) {return Boolean.TRUE;} else {//注意:除去true,1,与null全部返回falsereturn Boolean.FALSE;}} else if(Number.class.isAssignableFrom(toType)){if (!StringUtils.hasText(strValue) || "null".equals(strValue)) {return null;} return workBasicConverter.convertValue(context, target, member, property, strValue, toType);} else if (toType.isArray() || Collection.class.isAssignableFrom(toType)) {//向台向后台转,并且是单参数(即序列转数组)String sequence = strValue;//因为前台序列向后台转报空指针异常,改变逻辑为:如果前台传递空字符串,则变成空列表处理;而"null"字符串转换为null 还没有改写if (!StringUtils.hasText(sequence)||"null".equals(sequence)) {return null;//对序列情况的处理}//这里重写了拆分逻辑,将它们拆分成一个新数组value = new SequenceSplitor(sequence).split(SEQUENCE_SEPARATOR.charAt(0));return workBasicConverter.convertValue(context, target, member, property, value, toType);} else if (Map.class.isAssignableFrom(toType)) {//超类并没有给出对map情况的处理,这里直接将它处理掉,但是map中的成员仍然是依赖超类处理return convertToMap(context, target, member, property, strValue, toType);}throw new RuntimeException("预期外的类型");}//将一个序列转换成一个Mapprivate Object convertToMap(Map<String, Object> context, Object target, Member member, String property, String sequence, Class<?> toType) {String[] splitArray = new SequenceSplitor(sequence).split(SEQUENCE_SEPARATOR.charAt(0));Class<?> keyClass = String.class;Class<?> valueClass = String.class;if (target != null) {keyClass = this.objectTypeDeterminer.getKeyClass(target.getClass(), property);valueClass = this.objectTypeDeterminer.getElementClass(target.getClass(), property, null);}Map<Object, Object> map = createMap(context, target, member, property, toType, splitArray.length);for (int index = 0; index < splitArray.length; index++) {String keyValue = splitArray[index];//这里重写了拆分逻辑String[] keyValuePair = new SequenceSplitor(keyValue).split(MAP_SEPARATOR.charAt(0));//注意这个地方注入xworkConverter来进行转换,主要是怕遗漏Map中的键值有可能需要定制转换器,还有一点需要注意,传值使用字符串数组以模拟前台Object key = workBasicConverter.convertValue(context, null, null, null, keyValuePair[0], keyClass);Object val = workBasicConverter.convertValue(context, null, null, null, keyValuePair[1], valueClass);map.put(key, val);}return map;}private Map<Object, Object> createMap(Map<?, ?> context, Object target, Member member, String property, Class<?> toType, int size) {Map<Object, Object> map;if (toType == ConcurrentMap.class) {if (size > 0) {map = new ConcurrentHashMap<Object, Object>(size);} else {map = new ConcurrentHashMap<Object, Object>();}} else if (toType == SortedMap.class) {map = new TreeMap<Object, Object>();} else {if (size > 0) {map = new HashMap<Object, Object>(size);} else {map = new HashMap<Object, Object>();}}return map;}}


 

上面不再使用继承而是直接实现TypeConverter 接口需要先前超类完成的工作,使用注入进来的XWorkBasicConverter 来进行完成就可以,无论采用哪一种方式基本思想不变.


 

原创粉丝点击