Struts2中各种各样的参数传递

本篇主要通过实例来讲述Struts2中各种各样的参数传递。这个参数传递的过程主要指数据从View层传递到Control层时Struts2的工作方式。根据前两篇文章的知识,我们知道,Struts2完成参数传递处理工作的基础是OGNL和ValueStack。而在这个过程中,我也把Struts2所要做的工作大致归纳为两个方面:

1. 对OGNL操作进行封装,完成OGNL表达式所表示的值到Java对象的值传递机制

2. 在参数传递的过程中,做恰当的类型转化,保证页面上的字符串能够转化成各式各样的Java对象


接下来,通过四个不同的角度,来具体讲述Struts2在这两个方面的工作。

最简单的参数传递 Top

使用OGNL的最基本的功能,就能完成普通的Java对象的赋值工作。Struts2在内部已经完成了OGNL的基本封装。这些封装包括对OGNL表达式到Java对象的赋值机制,以及对基本的Java类型的类型转化支持。这些基本类型包括String、Number(以及其基本类型int、float、double等)、Boolean(boolean)、数组、Class、Date等类型。

在这里我想额外强调的是XWork对JDK5.0中的Enum类型和Date类型的支持。

Enum类型

枚举类型是JDK5.0引入的新特性。枚举类型也能解决很多实际问题,是J2EE编程中的最佳实践之一。XWork中,有一个专门的EnumTypeConverter负责对Enum类型的数据进行转化。

Java代码 复制代码
  1. public class EnumTypeConverter extends DefaultTypeConverter {   
  2.   
  3.     /**  
  4.      * Converts the given object to a given type. How this is to be done is implemented in toClass. The OGNL context, o  
  5.      * and toClass are given. This method should be able to handle conversion in general without any context or object  
  6.      * specified.  
  7.      *  
  8.      * @param context - OGNL context under which the conversion is being done  
  9.      * @param o       - the object to be converted  
  10.      * @param toClass - the class that contains the code to convert to enumeration  
  11.      * @return Converted value of type declared in toClass or TypeConverter.NoConversionPossible to indicate that the  
  12.      *         conversion was not possible.  
  13.      */  
  14.     public Object convertValue(Map context, Object o, Class toClass) {   
  15.         if (o instanceof String[]) {   
  16.             return convertFromString(((String[]) o)[0], toClass);   
  17.         } else if (o instanceof String) {   
  18.             return convertFromString((String) o, toClass);   
  19.         }   
  20.   
  21.         return super.convertValue(context, o, toClass);   
  22.     }   
  23.   
  24.     /**  
  25.      * Converts one or more String values to the specified class.  
  26.      * @param value - the String values to be converted, such as those submitted from an HTML form  
  27.      * @param toClass - the class to convert to  
  28.      * @return the converted object  
  29.      */  
  30.     public java.lang.Enum convertFromString(String value, Class toClass) {   
  31.         return Enum.valueOf(toClass, value);   
  32.     }   
  33.   
  34. }  
public class EnumTypeConverter extends DefaultTypeConverter {

    /**
     * Converts the given object to a given type. How this is to be done is implemented in toClass. The OGNL context, o
     * and toClass are given. This method should be able to handle conversion in general without any context or object
     * specified.
     *
     * @param context - OGNL context under which the conversion is being done
     * @param o       - the object to be converted
     * @param toClass - the class that contains the code to convert to enumeration
     * @return Converted value of type declared in toClass or TypeConverter.NoConversionPossible to indicate that the
     *         conversion was not possible.
     */
    public Object convertValue(Map context, Object o, Class toClass) {
        if (o instanceof String[]) {
            return convertFromString(((String[]) o)[0], toClass);
        } else if (o instanceof String) {
            return convertFromString((String) o, toClass);
        }

        return super.convertValue(context, o, toClass);
    }

    /**
     * Converts one or more String values to the specified class.
     * @param value - the String values to be converted, such as those submitted from an HTML form
     * @param toClass - the class to convert to
     * @return the converted object
     */
    public java.lang.Enum convertFromString(String value, Class toClass) {
        return Enum.valueOf(toClass, value);
    }

}


有了这个类,我们就可以比较轻松的对枚举类型进行数据赋值了。

Java代码 复制代码
  1. public enum Gender {   
  2.        
  3.     MALE, FEMALE   
  4.   
  5. }  
public enum Gender {
    
    MALE, FEMALE

}


Html代码 复制代码
  1. <form method="post" action="/struts-example/enum-conversion.action">  
  2.     <input type="text" name="user.name" value="downpour" />  
  3.     <select name="user.gender">  
  4.        <option value="MALE"></option>  
  5.        <option value="FEMALE"></option>  
  6.     </select>  
  7.     <input type="submit" value="submit" />  
  8. </form>  
<form method="post" action="/struts-example/enum-conversion.action">
    <input type="text" name="user.name" value="downpour" />
    <select name="user.gender">
       <option value="MALE">男</option>
       <option value="FEMALE">女</option>
    </select>
    <input type="submit" value="submit" />
</form>


Java代码 复制代码
  1. public class EnumConversionAction extends ActionSupport {   
  2.   
  3.     private static final Log logger = LogFactory.getLog(Policy.class);   
  4.        
  5.     private User user;   
  6.        
  7.     /* (non-Javadoc)  
  8.      * @see com.opensymphony.xwork2.ActionSupport#execute()  
  9.      */  
  10.     @Override  
  11.     public String execute() throws Exception {   
  12.         logger.info("user's gender:" + user.getGender());   
  13.         return super.execute();   
  14.     }   
  15.        
  16.     // setters and getters   
  17. }  
public class EnumConversionAction extends ActionSupport {

    private static final Log logger = LogFactory.getLog(Policy.class);
    
    private User user;
    
    /* (non-Javadoc)
     * @see com.opensymphony.xwork2.ActionSupport#execute()
     */
    @Override
    public String execute() throws Exception {
        logger.info("user's gender:" + user.getGender());
        return super.execute();
    }
    
    // setters and getters
}


通过上面的代码,就完成了对枚举类型的赋值。不过这里有一点需要特别指出:那就是XWork在XWork-2.1.X的版本之前,枚举类型不被默认支持。如果你需要获得枚举类型的自动赋值,还需要增加一个配置文件xwork-conversion.properties到classpath下:
Java代码 复制代码
  1. java.lang.Enum=com.opensymphony.xwork2.util.EnumTypeConverter  
java.lang.Enum=com.opensymphony.xwork2.util.EnumTypeConverter

对于使用新的版本的XWork的朋友,则不需要增加这个配置文件。

Date类型

XWork默认是支持Date类型的转化的。不过从源码上来看,貌似我们很难用上它默认的类型转化。

Java代码 复制代码
  1. private Object doConvertToDate(Map context, Object value, Class toType) {   
  2.         Date result = null;   
  3.   
  4.         if (value instanceof String && value != null && ((String) value).length() > 0) {   
  5.             String sa = (String) value;   
  6.             Locale locale = getLocale(context);   
  7.   
  8.             DateFormat df = null;   
  9.             if (java.sql.Time.class == toType) {   
  10.                 df = DateFormat.getTimeInstance(DateFormat.MEDIUM, locale);   
  11.             } else if (java.sql.Timestamp.class == toType) {   
  12.                 Date check = null;   
  13.                 SimpleDateFormat dtfmt = (SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.SHORT,   
  14.                         DateFormat.MEDIUM,   
  15.                         locale);   
  16.                 SimpleDateFormat fullfmt = new SimpleDateFormat(dtfmt.toPattern() + MILLISECOND_FORMAT,   
  17.                         locale);   
  18.   
  19.                 SimpleDateFormat dfmt = (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.SHORT,   
  20.                         locale);   
  21.   
  22.                 SimpleDateFormat[] fmts = {fullfmt, dtfmt, dfmt};   
  23.                 for (int i = 0; i < fmts.length; i++) {   
  24.                     try {   
  25.                         check = fmts[i].parse(sa);   
  26.                         df = fmts[i];   
  27.                         if (check != null) {   
  28.                             break;   
  29.                         }   
  30.                     } catch (ParseException ignore) {   
  31.                     }   
  32.                 }   
  33.             } else if (java.util.Date.class == toType) {   
  34.                 Date check = null;   
  35.                 SimpleDateFormat d1 = (SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.LONG, locale);   
  36.                 SimpleDateFormat d2 = (SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM, locale);   
  37.                 SimpleDateFormat d3 = (SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, locale);   
  38.                 SimpleDateFormat rfc3399 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");   
  39.                 SimpleDateFormat[] dfs = {d1, d2, d3, rfc3399}; //added RFC 3339 date format (XW-473)   
  40.                 for (int i = 0; i < dfs.length; i++) {   
  41.                     try {   
  42.                         check = dfs[i].parse(sa);   
  43.                         df = dfs[i];   
  44.                         if (check != null) {   
  45.                             break;   
  46.                         }   
  47.                     }   
  48.                     catch (ParseException ignore) {   
  49.                     }   
  50.                 }   
  51.             }   
  52.             //final fallback for dates without time   
  53.             if (df == null) {   
  54.                 df = DateFormat.getDateInstance(DateFormat.SHORT, locale);   
  55.             }   
  56.             try {   
  57.                 df.setLenient(false); // let's use strict parsing (XW-341)   
  58.                 result = df.parse(sa);   
  59.                 if (!(Date.class == toType)) {   
  60.                     try {   
  61.                         Constructor constructor = toType.getConstructor(new Class[]{long.class});   
  62.                         return constructor.newInstance(new Object[]{new Long(result.getTime())});   
  63.                     } catch (Exception e) {   
  64.                         throw new XWorkException("Couldn't create class " + toType + " using default (long) constructor", e);   
  65.                     }   
  66.                 }   
  67.             } catch (ParseException e) {   
  68.                 throw new XWorkException("Could not parse date", e);   
  69.             }   
  70.         } else if (Date.class.isAssignableFrom(value.getClass())) {   
  71.             result = (Date) value;   
  72.         }   
  73.         return result;   
  74.     }  
private Object doConvertToDate(Map context, Object value, Class toType) {
        Date result = null;

        if (value instanceof String && value != null && ((String) value).length() > 0) {
            String sa = (String) value;
            Locale locale = getLocale(context);

            DateFormat df = null;
            if (java.sql.Time.class == toType) {
                df = DateFormat.getTimeInstance(DateFormat.MEDIUM, locale);
            } else if (java.sql.Timestamp.class == toType) {
                Date check = null;
                SimpleDateFormat dtfmt = (SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.SHORT,
                        DateFormat.MEDIUM,
                        locale);
                SimpleDateFormat fullfmt = new SimpleDateFormat(dtfmt.toPattern() + MILLISECOND_FORMAT,
                        locale);

                SimpleDateFormat dfmt = (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.SHORT,
                        locale);

                SimpleDateFormat[] fmts = {fullfmt, dtfmt, dfmt};
                for (int i = 0; i < fmts.length; i++) {
                    try {
                        check = fmts[i].parse(sa);
                        df = fmts[i];
                        if (check != null) {
                            break;
                        }
                    } catch (ParseException ignore) {
                    }
                }
            } else if (java.util.Date.class == toType) {
                Date check = null;
                SimpleDateFormat d1 = (SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.LONG, locale);
                SimpleDateFormat d2 = (SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM, locale);
                SimpleDateFormat d3 = (SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, locale);
                SimpleDateFormat rfc3399 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
                SimpleDateFormat[] dfs = {d1, d2, d3, rfc3399}; //added RFC 3339 date format (XW-473)
                for (int i = 0; i < dfs.length; i++) {
                    try {
                        check = dfs[i].parse(sa);
                        df = dfs[i];
                        if (check != null) {
                            break;
                        }
                    }
                    catch (ParseException ignore) {
                    }
                }
            }
            //final fallback for dates without time
            if (df == null) {
                df = DateFormat.getDateInstance(DateFormat.SHORT, locale);
            }
            try {
                df.setLenient(false); // let's use strict parsing (XW-341)
                result = df.parse(sa);
                if (!(Date.class == toType)) {
                    try {
                        Constructor constructor = toType.getConstructor(new Class[]{long.class});
                        return constructor.newInstance(new Object[]{new Long(result.getTime())});
                    } catch (Exception e) {
                        throw new XWorkException("Couldn't create class " + toType + " using default (long) constructor", e);
                    }
                }
            } catch (ParseException e) {
                throw new XWorkException("Could not parse date", e);
            }
        } else if (Date.class.isAssignableFrom(value.getClass())) {
            result = (Date) value;
        }
        return result;
    }


这段代码就是XWork处理将String转成Date类型的过程,从整个过程上来看,我们很难用上这段代码,因为我们在界面上的Date类型的表现形式往往是:'yyyy-MM-dd'或者相关的形式,很明显,上面的流程无法匹配这样的日期类型。

所以,针对Date,我们往往会自定义一个日期转化的类进行处理,这个在下面会有具体的介绍。

Array、List、Map等容器类型的参数传递 Top

除了简单的基于JavaBean方式的参数传递支持,Struts2还支持对Array、List、Map等容器类型的数据结构做数据赋值。不过历史一路走来,XWork针对容器类型的数据赋值一直有变化,让我们慢慢解读这些变化,从而也来看看编程思路是如何改变的。

1. 2004年,XWork-1.0.X的年代

当时XWork所支持的针对容器的数据赋值还比较土。这方面moxie在论坛上有一篇文章专门来讲述: http://www.iteye.com/topic/8770

总的来说,那个年代对于容器的数据赋值,需要依赖于XWork的辅助类。我们可以看到,如果你要对List进行赋值,需要新建一个XWorkList的实现类,并把所需要进行数据赋值的Java类传递到XWorkList的构造函数中。而对Map等对象的赋值,也同理可得。

这种数据赋值的方式的优缺点都非常明显。优点在于 简单,你不需要额外定义任何其他的内容,而是直接使用XWork的辅助类来实现类型转化。缺点在于 扩展性很弱,很明显,针对某一个具体的容器,就需要一个XWork的实现类,List有XWorkList对应,Map有XWorkMap对应。甚至在那个时候,还没有Set的支持,因为没有XWorkSet的实现。所以使用这种方式,在扩展性方面需要遭受严重的考验。

2. 2006年,XWork-2.0.X的年代

也许是XWork团队看到了扩展性上的问题,所以在XWork和Webwork同时升级以后,采用了新的方式来处理容器赋值。而此时,Javaeye上也涌现出了新的文章,Tin同学对新的方式做了详细的表述: http://www.iteye.com/topic/17939

不过这个新的整合方式似乎并不被大家所看好。

lllyq 写道
我觉得XWorkList, XWorkMap还是很有用的,挺好的设计,其实没有必要deprecated。


moxie 写道
集合支持不向下兼容。XWorkList已经是@deprecated,用它就错,还不如直接删除掉。在webwork2.2中,它需要为集合另外配置一个conversion.properties文件。真不明白,这样有什么优点?


这种新的整合方式,实际上只是解决了针对容器赋值,不需要依赖XWork的辅助类这样的一个问题,不过其付出的代价,却是多了一个配置文件,这也让人非常郁闷。好好的类型转化,平白无故多出了一个同package下的配置文件,这也无形中增加了编程的复杂度。

3. 现在,拥抱了泛型和Annotation的年代

实际上,在XWork发展到XWork-2.0.X之后,也开始注重了对泛型和Annotation的支持。所以,容器类型的转化,我们也可以尝试一下使用JDK的新特性来进行,当然这也是目前最为推荐的做法。

下面分别给出使用泛型和Annotation的代码示例:

Html代码 复制代码
  1. <form method="post" action="/struts-example/ognl-collection-conversion.action">  
  2.     <input type="text" name="users[0].name" value="aaa" />  
  3.     <input type="text" name="users[1].name" value="bbb" />  
  4.     <input type="text" name="users2[0].name" value="ccc" />  
  5.     <input type="text" name="users2[1].name" value="ddd" />  
  6.     <input type="text" name="userMap['user1'].name" value="eee" />  
  7.     <input type="text" name="userMap['user2'].name" value="fff" />  
  8.     <input type="text" name="userMap2['user3'].name" value="ggg" />  
  9.     <input type="text" name="userMap2['user4'].name" value="hhh" />  
  10.     <input type="submit" value="submit" />  
  11. </form>  
<form method="post" action="/struts-example/ognl-collection-conversion.action">
    <input type="text" name="users[0].name" value="aaa" />
    <input type="text" name="users[1].name" value="bbb" />
    <input type="text" name="users2[0].name" value="ccc" />
    <input type="text" name="users2[1].name" value="ddd" />
    <input type="text" name="userMap['user1'].name" value="eee" />
    <input type="text" name="userMap['user2'].name" value="fff" />
    <input type="text" name="userMap2['user3'].name" value="ggg" />
    <input type="text" name="userMap2['user4'].name" value="hhh" />
    <input type="submit" value="submit" />
</form>


Java代码 复制代码
  1. public class OgnlConversionAction extends ActionSupport {   
  2.   
  3.     private static final long serialVersionUID = 4396125455881691845L;   
  4.   
  5.     private static final Log logger = LogFactory.getLog(Policy.class);   
  6.        
  7.     private List<User> users;   
  8.        
  9.     @Element(value = User.class)   
  10.     private List users2;   
  11.        
  12.     private Map<String, User> userMap;   
  13.        
  14.     @Element(value = User.class)   
  15.     private Map userMap2;   
  16.        
  17.     /* (non-Javadoc)  
  18.      * @see com.opensymphony.xwork2.ActionSupport#execute()  
  19.      */  
  20.     @Override  
  21.     public String execute() throws Exception {   
  22.            
  23.         // -> aaa   
  24.         logger.info("users[0].name : " + users.get(0).getName());   
  25.         // -> bbb   
  26.         logger.info("users[1].name : " + users.get(
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值