- 浏览: 38822 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (24)
- Java基础 (7)
- Struts2 (5)
- Spring2.0 (2)
- SpringSide (0)
- Hibernate (1)
- CSS (0)
- DB2 (1)
- DWR (0)
- JQuery (0)
- EXTJS (0)
- Flex (0)
- JBPM (0)
- JFM (0)
- JMS (0)
- JNDI (0)
- JSON (0)
- JUnit (0)
- Maven (0)
- MQ (0)
- NIO (1)
- LDAP (0)
- Portal (0)
- WebService (2)
- Java 规则引擎 (1)
- 精彩生活 (1)
- WebGIS (0)
- Eclipse (1)
- Web前端 (0)
- 兴趣文章 (1)
- HTML (0)
- Android2.x (0)
- JavaScript (0)
最新评论
-
makemyownlife:
写的不错 哈哈
struts2 参数传递 赋值 类型转化 -
eason26_li:
太深奥了
人工神经网络的典型模型
转载:http://struts2.group.iteye.com/group/wiki/1365-passing-parameters-in-struts2
struts2 参数传递 赋值 类型转化
本篇主要通过实例来讲述Struts2中各种各样的参数传递。这个参数传递的过程主要指数据从View层传递到Control层时Struts2的工作方式。根据前两篇文章的知识,我们知道,Struts2完成参数传递处理工作的基础是OGNL和ValueStack。而在这个过程中,我也把Struts2所要做的工作大致归纳为两个方面:
1. 对OGNL操作进行封装,完成OGNL表达式所表示的值到Java对象的值传递机制
2. 在参数传递的过程中,做恰当的类型转化,保证页面上的字符串能够转化成各式各样的Java对象
接下来,通过四个不同的角度,来具体讲述Struts2在这两个方面的工作。
1. 对OGNL操作进行封装,完成OGNL表达式所表示的值到Java对象的值传递机制
2. 在参数传递的过程中,做恰当的类型转化,保证页面上的字符串能够转化成各式各样的Java对象
接下来,通过四个不同的角度,来具体讲述Struts2在这两个方面的工作。
最简单的参数传递 更多
使用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类型的数据进行转化。
有了这个类,我们就可以比较轻松的对枚举类型进行数据赋值了。
通过上面的代码,就完成了对枚举类型的赋值。不过这里有一点需要特别指出:那就是XWork在XWork-2.1.X的版本之前,枚举类型不被默认支持。如果你需要获得枚举类型的自动赋值,还需要增加一个配置文件xwork-conversion.properties到classpath下:
对于使用新的版本的XWork的朋友,则不需要增加这个配置文件。
Date类型
XWork默认是支持Date类型的转化的。不过从源码上来看,貌似我们很难用上它默认的类型转化。
这段代码就是XWork处理将String转成Date类型的过程,从整个过程上来看,我们很难用上这段代码,因为我们在界面上的Date类型的表现形式往往是:'yyyy-MM-dd'或者相关的形式,很明显,上面的流程无法匹配这样的日期类型。
所以,针对Date,我们往往会自定义一个日期转化的类进行处理,这个在下面会有具体的介绍。
在这里我想额外强调的是XWork对JDK5.0中的Enum类型和Date类型的支持。
Enum类型
枚举类型是JDK5.0引入的新特性。枚举类型也能解决很多实际问题,是J2EE编程中的最佳实践之一。XWork中,有一个专门的EnumTypeConverter负责对Enum类型的数据进行转化。
- 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);
- }
- }
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); } }
有了这个类,我们就可以比较轻松的对枚举类型进行数据赋值了。
public enum Gender { MALE, FEMALE }
- <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>
<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>
- 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
- }
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.lang.Enum=com.opensymphony.xwork2.util.EnumTypeConverter
对于使用新的版本的XWork的朋友,则不需要增加这个配置文件。
Date类型
XWork默认是支持Date类型的转化的。不过从源码上来看,貌似我们很难用上它默认的类型转化。
- 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;
- }
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等容器类型的参数传递 更多
除了简单的基于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。
不过这个新的整合方式似乎并不被大家所看好。
这种新的整合方式,实际上只是解决了针对容器赋值,不需要依赖XWork的辅助类这样的一个问题,不过其付出的代价,却是多了一个配置文件,这也让人非常郁闷。好好的类型转化,平白无故多出了一个同package下的配置文件,这也无形中增加了编程的复杂度。
3. 现在,拥抱了泛型和Annotation的年代
实际上,在XWork发展到XWork-2.0.X之后,也开始注重了对泛型和Annotation的支持。所以,容器类型的转化,我们也可以尝试一下使用JDK的新特性来进行,当然这也是目前最为推荐的做法。
下面分别给出使用泛型和Annotation的代码示例:
上面的代码中,我们可以看到,如果你使用泛型,那么你无需再使用任何额外的配置文件或者Annotation,XWork会把一切都为你准备好。如果你没有使用泛型,那么你可以使用Annotation来指定你需要进行转化的对象类型。其中,对Map对象使用Annotation时,Element中的value所对应的值,是Map中的value所对应的class。
由此可见,泛型和Annotation,在一定程度上,还是可以简化我们很多工作的。
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的代码示例:
- <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>
<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>
- public class OgnlConversionAction extends ActionSupport {
- private static final long serialVersionUID = 4396125455881691845L;
- private static final Log logger = LogFactory.getLog(Policy.class);
- private List<User> users;
- @Element(value = User.class)
- private List users2;
- private Map<String, User> userMap;
- @Element(value = User.class)
- private Map userMap2;
- /* (non-Javadoc)
- * @see com.opensymphony.xwork2.ActionSupport#execute()
- */
- @Override
- public String execute() throws Exception {
- // -> aaa
- logger.info("users[0].name : " + users.get(0).getName());
- // -> bbb
- logger.info("users[1].name : " + users.get(1).getName());
- // -> ccc
- logger.info("users2[0].name : " + ((User)users2.get(0)).getName());
- // -> ddd
- logger.info("users2[1].name : " + ((User)users2.get(1)).getName());
- // -> [user1, user2]
- logger.info("userMap.key : " + userMap.keySet());
- // -> eee
- logger.info("userMap.key = " + "user1" + " : " + "userMap.value(user1's name) = " + userMap.get("user1").getName());
- // -> fff
- logger.info("userMap.key = " + "user2" + " : " + "userMap.value(user2's name) = " + userMap.get("user2").getName());
- // -> [user3, user4]
- logger.info("userMap2.key : " + userMap2.keySet());
- // -> ggg
- logger.info("userMap2.key = " + "user3" + " : " + "userMap.value(user3's name) = " + ((User)userMap2.get("user3")).getName());
- // -> hhh
- logger.info("userMap2.key = " + "user4" + " : " + "userMap.value(user4's name) = " + ((User)userMap2.get("user4")).getName());
- return super.execute();
- }
- // setters and getters
- }
public class OgnlConversionAction extends ActionSupport { private static final long serialVersionUID = 4396125455881691845L; private static final Log logger = LogFactory.getLog(Policy.class); private List<User> users; @Element(value = User.class) private List users2; private Map<String, User> userMap; @Element(value = User.class) private Map userMap2; /* (non-Javadoc) * @see com.opensymphony.xwork2.ActionSupport#execute() */ @Override public String execute() throws Exception { // -> aaa logger.info("users[0].name : " + users.get(0).getName()); // -> bbb logger.info("users[1].name : " + users.get(1).getName()); // -> ccc logger.info("users2[0].name : " + ((User)users2.get(0)).getName()); // -> ddd logger.info("users2[1].name : " + ((User)users2.get(1)).getName()); // -> [user1, user2] logger.info("userMap.key : " + userMap.keySet()); // -> eee logger.info("userMap.key = " + "user1" + " : " + "userMap.value(user1's name) = " + userMap.get("user1").getName()); // -> fff logger.info("userMap.key = " + "user2" + " : " + "userMap.value(user2's name) = " + userMap.get("user2").getName()); // -> [user3, user4] logger.info("userMap2.key : " + userMap2.keySet()); // -> ggg logger.info("userMap2.key = " + "user3" + " : " + "userMap.value(user3's name) = " + ((User)userMap2.get("user3")).getName()); // -> hhh logger.info("userMap2.key = " + "user4" + " : " + "userMap.value(user4's name) = " + ((User)userMap2.get("user4")).getName()); return super.execute(); } // setters and getters }
上面的代码中,我们可以看到,如果你使用泛型,那么你无需再使用任何额外的配置文件或者Annotation,XWork会把一切都为你准备好。如果你没有使用泛型,那么你可以使用Annotation来指定你需要进行转化的对象类型。其中,对Map对象使用Annotation时,Element中的value所对应的值,是Map中的value所对应的class。
由此可见,泛型和Annotation,在一定程度上,还是可以简化我们很多工作的。
文件上传 更多
文件上传其实也是参数传递的一种,所以从方案上来讲,Struts2同样使用了一个拦截器来处理。而这个拦截器,同样来自于原来的Webwork,基本上没有做什么很大的改变。有关这个拦截器的详细内容,我们也留一个悬念,在后续章节中详细讲解。目前,你只要知晓,这个拦截器能帮助你完成一切文件上传相关的机制。
早在2005年,Quake Wang就对Webwork的文件上传机制有了详细的讲解:http://www.iteye.com/topic/10697
在这里我简单小结一下在进行文件上传时的三大要点:
1. 在配置文件上传时,拦截器的顺序非常关键
具体来说,upload的拦截器,必须在params的拦截器之前
2. 拦截器额外提供了一些额外的文件信息
3. 拦截器提供的文件上传功能,你得到的是一个临时文件
而时代发展到Struts2的年代,对于文件上传的整体机制没有做什么改变。只是Struts2将apache的common-fileupload作为了其默认的文件上传的机制。
例子归例子,实际情况中,我们还是会遇到一些问题:
1. 默认实现中,文件和文件信息是分开表述的,对于后台处理来说,不是非常方便
2. common-fileupload的实现,虽然提供了文件上传的机制,也可以让你得到文件的一些属性信息,但是它无法得到客户端的上传路径
对于第一个问题,我们可以使用OGNL的特性,将这些文件和文件名等文件信息做封装:
在这个类中,我定义了upload表示上传的文件,fileName表示上传文件的文件名。请注意我整个文件中的最后一个方法:setUploadFileName。这个方法将保证FileUploadInterceptor在运行时,能够正确设置上传的文件名。
这样,在Action中,我们将面对一个个完整的fileComponent对象,其中包括文件的引用、文件名称和其他文件信息。这样就不会因为上传多个文件而手足无措,你只需要使用fileComponent数组,就能轻松对上传的文件进行管理,而避免了在Action中书写许多个文件、文件名等属性了。
对于第二个问题,目前我也没有找到很好的方法。我所采用的方式与Yulimin是一致的:
早在2005年,Quake Wang就对Webwork的文件上传机制有了详细的讲解:http://www.iteye.com/topic/10697
在这里我简单小结一下在进行文件上传时的三大要点:
1. 在配置文件上传时,拦截器的顺序非常关键
- <interceptor-stack name="uploadStack">
- <interceptor-ref name="upload"/>
- <interceptor-ref name="defaultStack"/>
- </interceptor-stack>
<interceptor-stack name="uploadStack"> <interceptor-ref name="upload"/> <interceptor-ref name="defaultStack"/> </interceptor-stack>
具体来说,upload的拦截器,必须在params的拦截器之前
2. 拦截器额外提供了一些额外的文件信息
Quake Wang 写道
ContentType: 文件的ContentType(可以用在做download的时候)
FileName: 实际的文件名
在上面的action例子里, 那么有uploadFilesContentType和uploadFilesFileName这2个属性, 也能够被自动绑定
FileName: 实际的文件名
在上面的action例子里, 那么有uploadFilesContentType和uploadFilesFileName这2个属性, 也能够被自动绑定
3. 拦截器提供的文件上传功能,你得到的是一个临时文件
robbin 写道
在webwork的file upload 拦截器功能中,它提供的File只是一个临时文件,Action执行之后就会被自动删除,因此你必须在Action中自己出来文件的存储问题,或者写到服务器的某个目录,或者保存到数据库中。如果你准备写到服务器的某个目录下面的话,你必须自己面临着处理文件同名的问题
而时代发展到Struts2的年代,对于文件上传的整体机制没有做什么改变。只是Struts2将apache的common-fileupload作为了其默认的文件上传的机制。
例子归例子,实际情况中,我们还是会遇到一些问题:
1. 默认实现中,文件和文件信息是分开表述的,对于后台处理来说,不是非常方便
2. common-fileupload的实现,虽然提供了文件上传的机制,也可以让你得到文件的一些属性信息,但是它无法得到客户端的上传路径
对于第一个问题,我们可以使用OGNL的特性,将这些文件和文件名等文件信息做封装:
- public class FileComponent implements Serializable {
- private static final long serialVersionUID = 4594003082271182188L;
- private File upload;
- private String fileName;
- /**
- * The default constructor
- */
- public FileComponent() {
- }
- /**
- * @return Returns the upload.
- */
- public File getUpload() {
- return upload;
- }
- /**
- * @return Returns the fileName.
- */
- public String getFileName() {
- return fileName;
- }
- /**
- * @param upload
- * The upload to set.
- */
- public void setUpload(File upload) {
- this.upload = upload;
- }
- /**
- * @param fileName
- * The fileName to set.
- */
- public void setFileName(String fileName) {
- this.fileName = fileName;
- }
- /**
- * @param fileName
- * The fileName to set.
- */
- public void setUploadFileName(String uploadFileName) {
- this.fileName = uploadFileName;
- }
- }
public class FileComponent implements Serializable { private static final long serialVersionUID = 4594003082271182188L; private File upload; private String fileName; /** * The default constructor */ public FileComponent() { } /** * @return Returns the upload. */ public File getUpload() { return upload; } /** * @return Returns the fileName. */ public String getFileName() { return fileName; } /** * @param upload * The upload to set. */ public void setUpload(File upload) { this.upload = upload; } /** * @param fileName * The fileName to set. */ public void setFileName(String fileName) { this.fileName = fileName; } /** * @param fileName * The fileName to set. */ public void setUploadFileName(String uploadFileName) { this.fileName = uploadFileName; } }
在这个类中,我定义了upload表示上传的文件,fileName表示上传文件的文件名。请注意我整个文件中的最后一个方法:setUploadFileName。这个方法将保证FileUploadInterceptor在运行时,能够正确设置上传的文件名。
- /**
- * @param fileName
- * The fileName to set.
- */
- public void setUploadFileName(String uploadFileName) {
- this.fileName = uploadFileName;
- }
/** * @param fileName * The fileName to set. */ public void setUploadFileName(String uploadFileName) { this.fileName = uploadFileName; }
这样,在Action中,我们将面对一个个完整的fileComponent对象,其中包括文件的引用、文件名称和其他文件信息。这样就不会因为上传多个文件而手足无措,你只需要使用fileComponent数组,就能轻松对上传的文件进行管理,而避免了在Action中书写许多个文件、文件名等属性了。
对于第二个问题,目前我也没有找到很好的方法。我所采用的方式与Yulimin是一致的:
Yulimin 写道
我现在的做法是表单中增加了一个隐藏域,当用户文件选择后,利用JS截取到用户选择的文件名,然后一起提交上去。
不知道有没有最终的解决方法?
不知道有没有最终的解决方法?
自定义的类型转化实现 更多
Struts2在处理参数传递的过程中,需要完成类型转化,保证页面上的字符串能够转化成各式各样的Java对象。而这一点,其实也是由OGNL完成的。还记得我们在讲述OGNL的基础知识的时候列出来过的一个接口嘛?
在这个接口中,我们可以在使用OGNL的时候,注册针对某个Class级别的自己实现的TypeConverter,这样,OGNL就会在进行设值计算和取值计算的时候,使用自定义的类型转化方式了。让我们来看看TypeConverter的接口定义:
知道了原理,就简单了,我们可以自己实现一个TypeConverter的实现类,并且在Struts2中注册一下使用这个TypeConverter的Java类型的对应关系,我们就可以完成自定义的类型转化了。
具体的例子,可以参考一下Quake Wang同学在2005年时的一篇文章:http://www.iteye.com/topic/10507。文章针对的是Webwork2,但是无论是实现方式还是操作步骤,与Struts2是完全相同的。值得提醒的是,这篇文章的回复也非常有价值,在看文章的同时,不要忘记看回复。
不过针对Quake Wang的例子,我也想做一些补充。它的例子中,主要讲述了Struts2中如何去做java.utils.Date的自动类型转化,也正如后面回复中有人提到:
而Quake Wang对此是这样解决的:
我在这里提供一个我在实际项目中采用的方式:
在我采用的方式中,依然采用Application-wide conversion rules,只是在自定义的Converter中,根据不同的日期形式进行逐个匹配,找到第一个匹配的日期类型进行转化。当然,这是一种投机取巧的办法,不过比较实用,适用于项目中不太使用国际化,日期的形式不太复杂的情况。
- /**
- * Appends the standard naming context for evaluating an OGNL expression
- * into the context given so that cached maps can be used as a context.
- *
- * @param root the root of the object graph
- * @param context the context to which OGNL context will be added.
- * @return Context Map with the keys <CODE>root</CODE> and <CODE>context</CODE>
- * set appropriately
- */
- public static Map addDefaultContext( Object root, ClassResolver classResolver, TypeConverter converter, MemberAccess memberAccess, Map context );
/** * Appends the standard naming context for evaluating an OGNL expression * into the context given so that cached maps can be used as a context. * * @param root the root of the object graph * @param context the context to which OGNL context will be added. * @return Context Map with the keys <CODE>root</CODE> and <CODE>context</CODE> * set appropriately */ public static Map addDefaultContext( Object root, ClassResolver classResolver, TypeConverter converter, MemberAccess memberAccess, Map context );
在这个接口中,我们可以在使用OGNL的时候,注册针对某个Class级别的自己实现的TypeConverter,这样,OGNL就会在进行设值计算和取值计算的时候,使用自定义的类型转化方式了。让我们来看看TypeConverter的接口定义:
- /**
- * context - OGNL context under which the conversion is being done
- * target - target object in which the property is being set
- * member - member (Constructor, Method or Field) being set
- * propertyName - property name being set
- * value - value to be converted
- * toType - type to which value is converted
- */
- public Object convertValue(Map context, Object target, Member member, String propertyName, Object value, Class toType);
/** * context - OGNL context under which the conversion is being done * target - target object in which the property is being set * member - member (Constructor, Method or Field) being set * propertyName - property name being set * value - value to be converted * toType - type to which value is converted */ public Object convertValue(Map context, Object target, Member member, String propertyName, Object value, Class toType);
知道了原理,就简单了,我们可以自己实现一个TypeConverter的实现类,并且在Struts2中注册一下使用这个TypeConverter的Java类型的对应关系,我们就可以完成自定义的类型转化了。
具体的例子,可以参考一下Quake Wang同学在2005年时的一篇文章:http://www.iteye.com/topic/10507。文章针对的是Webwork2,但是无论是实现方式还是操作步骤,与Struts2是完全相同的。值得提醒的是,这篇文章的回复也非常有价值,在看文章的同时,不要忘记看回复。
不过针对Quake Wang的例子,我也想做一些补充。它的例子中,主要讲述了Struts2中如何去做java.utils.Date的自动类型转化,也正如后面回复中有人提到:
wolfsquare 写道
如果我在界面上有两种格式的日期怎么办?
例如一种短格式: SimpleDateFormat("yyyy-mm-dd"),一种长格式SimpleDateFormat("yyyy-mm-dd hh:MM:ss")
例如一种短格式: SimpleDateFormat("yyyy-mm-dd"),一种长格式SimpleDateFormat("yyyy-mm-dd hh:MM:ss")
而Quake Wang对此是这样解决的:
Quake Wang 写道
可以根据你的应用情况,看哪种方式是比较常见的转换规则,那么把这个规则定成Application-wide conversion rules:
另外的一个转换,可以写成Class-specific conversion rules :
otherDate=com.javaeye.core.webwork.converter.OtherDateConverter
- 在classpath root下面写一个xwork-conversion.properties:
- java.util.Date=com.javaeye.core.webwork.converter.DateConverter
在classpath root下面写一个xwork-conversion.properties: java.util.Date=com.javaeye.core.webwork.converter.DateConverter
另外的一个转换,可以写成Class-specific conversion rules :
otherDate=com.javaeye.core.webwork.converter.OtherDateConverter
我在这里提供一个我在实际项目中采用的方式:
- public class DateConverter extends DefaultTypeConverter {
- private static final Log logger = LogFactory.getLog(DateConverter.class);
- private static final String DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
- private static final String DATE_PATTERN = "yyyy-MM-dd";
- private static final String MONTH_PATTERN = "yyyy-MM";
- /**
- * Convert value between types
- */
- public Object convertValue(Map ognlContext, Object value, Class toType) {
- Object result = null;
- if (toType == Date.class) {
- result = doConvertToDate(value);
- } else if (toType == String.class) {
- result = doConvertToString(value);
- }
- return result;
- }
- /**
- * Convert String to Date
- *
- * @param value
- * @return
- */
- private Date doConvertToDate(Object value) {
- Date result = null;
- if (value instanceof String) {
- // TODO add date converter parse order here
- result = DateUtils.parseDate((String) value, new String[] { DATE_PATTERN, DATETIME_PATTERN, MONTH_PATTERN });
- // all patterns failed, try a milliseconds constructor
- if (result == null && StringUtils.isNotEmpty((String)value)) {
- try {
- result = new Date(new Long((String) value).longValue());
- } catch (Exception e) {
- logger.error("Converting from milliseconds to Date fails!");
- e.printStackTrace();
- }
- }
- } else if (value instanceof Object[]) {
- // let's try to convert the first element only
- Object[] array = (Object[]) value;
- if ((array != null) && (array.length >= 1)) {
- value = array[0];
- result = doConvertToDate(value);
- }
- } else if (Date.class.isAssignableFrom(value.getClass())) {
- result = (Date) value;
- }
- return result;
- }
- /**
- * Convert Date to String
- *
- * @param value
- * @return
- */
- private String doConvertToString(Object value) {
- SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DATETIME_PATTERN);
- String result = null;
- if (value instanceof Date) {
- result = simpleDateFormat.format(value);
- }
- return result;
- }
- }
public class DateConverter extends DefaultTypeConverter { private static final Log logger = LogFactory.getLog(DateConverter.class); private static final String DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss"; private static final String DATE_PATTERN = "yyyy-MM-dd"; private static final String MONTH_PATTERN = "yyyy-MM"; /** * Convert value between types */ public Object convertValue(Map ognlContext, Object value, Class toType) { Object result = null; if (toType == Date.class) { result = doConvertToDate(value); } else if (toType == String.class) { result = doConvertToString(value); } return result; } /** * Convert String to Date * * @param value * @return */ private Date doConvertToDate(Object value) { Date result = null; if (value instanceof String) { // TODO add date converter parse order here result = DateUtils.parseDate((String) value, new String[] { DATE_PATTERN, DATETIME_PATTERN, MONTH_PATTERN }); // all patterns failed, try a milliseconds constructor if (result == null && StringUtils.isNotEmpty((String)value)) { try { result = new Date(new Long((String) value).longValue()); } catch (Exception e) { logger.error("Converting from milliseconds to Date fails!"); e.printStackTrace(); } } } else if (value instanceof Object[]) { // let's try to convert the first element only Object[] array = (Object[]) value; if ((array != null) && (array.length >= 1)) { value = array[0]; result = doConvertToDate(value); } } else if (Date.class.isAssignableFrom(value.getClass())) { result = (Date) value; } return result; } /** * Convert Date to String * * @param value * @return */ private String doConvertToString(Object value) { SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DATETIME_PATTERN); String result = null; if (value instanceof Date) { result = simpleDateFormat.format(value); } return result; } }
在我采用的方式中,依然采用Application-wide conversion rules,只是在自定义的Converter中,根据不同的日期形式进行逐个匹配,找到第一个匹配的日期类型进行转化。当然,这是一种投机取巧的办法,不过比较实用,适用于项目中不太使用国际化,日期的形式不太复杂的情况。
相关推荐
7. **结合Interceptor**:ModelDriven可以与Struts2的拦截器(Interceptor)配合使用,例如,你可以在拦截器中进行参数校验、数据转换等操作,进一步增强模型数据的处理能力。 8. **自定义模型驱动**:如果默认的...
作者处处从实战出发,在丰富的示例中直观地探讨了许多实用的技术,如数据类型转换、文件上传和下载、提高Struts 2应用的安全性、调试与性能分析、FreeMarker、Velocity、Ajax,等等。跟随作者一道深入Struts 2,聆听...
5. **类型转换与参数绑定**:Struts2自动将请求参数绑定到Action类的属性,并进行类型转换,减少了手动赋值的工作量。 6. **异常处理**:通过全局异常映射,Struts2可以统一处理应用中的异常,提供一致的错误反馈。...
Struts2是基于MVC设计模式的一款开源Web应用框架,它继承了Struts1的优点,并且进行了大量的改进,支持拦截器、OGNL表达式语言、类型转换等特性。在实际工作中,Struts2通常用于构建企业级应用系统,特别是那些需要...
在Struts2框架中,OGNL作为默认的数据绑定和表达式语言,用于在Action类的属性与视图层(如JSP)之间传递数据。 2. OGNL的基本语法: OGNL支持多种操作,包括访问对象属性、调用方法、数组和集合操作、算术运算等...
如数据类型转换、文件上传和下载、Struts2应用的安全性、调试与性能分析、FreeMarker、Velocily、Ajax,等等。跟随作者一道深入Struts2。聆听大量来之不易的经验之谈。你对Struts2开发框架的理解和应用水平都将更上...
这种转换过程在处理用户输入时非常有用,因为HTTP请求通常是以字符串形式传递的,而我们的业务逻辑可能需要整数、日期或其他复杂类型的对象。 类型转换在WebWork中通过`ConversionAwareActionForm`和`TypeConverter...
模仿Struts的Servlet可能也提供了类似的机制,通过注解或者配置文件,可以自动将请求参数赋值给Servlet中的对象属性,简化了数据处理的步骤。 为了实现这个功能,Servlet可能使用了以下技术: 1. 请求参数解析:...
- 行5:`<param name="pageSize">3</param>` 这一行的作用是使用Struts2的配置注入功能,将字符串"3"赋值给`FeeListAction`类中名为`pageSize`的属性。 **4. Struts2项目构建** 为了实现NETCTOSS用户登录功能,...
2. Struts框架接收到请求,调用`FileUploadAction`,并将文件内容传递给Hibernate保存到MySQL的BLOB字段。 3. 保存成功后,返回页面显示文件已上传的提示。 4. 当用户请求下载文件时,触发`FileDownloadAction`,该...
4. **类型转换**:研究OGNL如何处理不同类型之间的转换,确保数据的正确传递。 五、源代码导入与学习步骤 1. **解压文件**:首先将下载的压缩包解压,通常会得到一个包含多个源代码文件和目录的结构。 2. **导入IDE...
- **Struts1和Struts2**:Struts1是基于ActionForm的MVC框架,Struts2结合了Struts和WebWork的优点,提供了更多功能和灵活性。 - **Session的get()和load()**:`get()`用于直接获取对象,如果对象不存在则返回null;...
Struts2基于类开发,通过类的属性传递参数。 - 数据存取:Spring MVC通过参数解析器将请求内容解析并赋值给方法形参;Struts2使用OGNL存取数据。 - 视图解析:Spring MVC默认使用JSTL,而Struts2有自己的视图解析...
4. **模块化设计**:类似于编程语言中的函数或方法,存储过程可以被重复调用,支持参数传递和返回值,提高了代码的复用性和可维护性。 #### 三、存储过程的语法规则 1. **创建存储过程**: ```sql CREATE ...
OGNL提供了丰富的功能,包括类型转换、对象属性的访问以及操作集合对象等。 在Struts2框架中,OGNL作为默认的表达式语言,具有以下几个显著优点: 1. 支持对象方法调用,允许开发者直接在表达式中执行对象的方法,...
- `不实用structs标签向jsp页面传值.txt`:在某些情况下,开发者可能选择不使用Struts2标签,而是直接通过EL(Expression Language)或JSTL(JavaServer Pages Standard Tag Library)来传递和显示数据。...
5. 方法:定义、调用方法,理解参数传递机制。 四、面向对象编程 1. 类与对象:了解类的定义、对象的创建,理解封装、继承和多态三大特性。 2. 构造器:用于初始化新创建的对象。 3. 访问修饰符:public、private、...
在重定向示例中,`<c:param name="userName" value="RW" />`向目标页面传递了一个名为`userName`的参数,值为`RW`。 10. **`<c:redirect>`**:重定向客户端请求到新的URL。示例中,`...