分享
 
 
 

在Struts框架下使用时间类型

王朝java/jsp·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

使用时间类型?这谁不会,不就是java.util下的几个类吗,在不加上java.sql和java.text下的几个类,这会有什么问题吗?Struts要是连

时间都处理不了,那还能干嘛? 在实际应用中,我就发现Struts确实连有些简单的时间都处理不了(不知是我使用的方法不对还是Struts确

实没有考虑到)。顺便你也能了解Struts是怎么把form里的请求参数populate到ActionForm里面的。

今天下午同事告诉我把有java.util.Date类型属性的类存入数据库时出错,把这个属性删除就没有问题了。当时我就想到是

RequestProcessor在processPopulate()时出错了,因此在它的这个方法设了断点并跟踪了进去。当然,它最先要调用ActionForm的reset()方

法,然后调用实际处理populate(将请求参数传给ActionForm)的RequestUtils.populate()方法。RequestUtils的这个静态方法最先是处理

Multipart的(即文件上传等多部分)的方法,然后将所有的请求都放在叫properties的HashMap里并循环处理它:

names = request.getParameterNames();

while (names.hasMoreElements()) {

String name = (String) names.nextElement();

String stripped = name;

if (prefix != null) {

if (!stripped.startsWith(prefix)) {

continue;

}

stripped = stripped.substring(prefix.length());

}

if (suffix != null) {

if (!stripped.endsWith(suffix)) {

continue;

}

stripped = stripped.substring(0, stripped.length() - suffix.length());

}

if (isMultipart) {

properties.put(stripped, multipartParameters.get(name));

} else {

properties.put(stripped, request.getParameterValues(name));

}

}

实际处理它们的是下面的:BeanUtils.populate(bean, properties); 其中bean就是接受数据的ActionForm,而properties里面则是

所有的请求的键-值对(键和值都是字符串,http协议的特点)。

再看看BeanUtils的静态(类)方法populate是怎么处理的:

// Loop through the property name/value pairs to be set

Iterator names = properties.keySet().iterator();

while (names.hasNext()) {

// Identify the property name and value(s) to be assigned

String name = (String) names.next();

if (name == null) {

continue;

}

Object value = properties.get(name);

// Perform the assignment for this property

setProperty(bean, name, value);

}

它是循环所有的请求参数,把实际的工作又交给了setProperty方法。呵呵,弄了半天,这帮人原来都是代理。

这个方法还是代理吗?计算了一下它有180行的代码。这么长应该是个实干家了吧,错!千万不要被有些人的外表欺骗了!有些人一天

上班16个小时,可够敬业的,可有8小时在打CS。这个类就是:一上来20多行都在一个if (log.isTraceEnabled()){}里面。

log在这说明一下。Struts中使用的是Jakarta Commons Logging的包,它使用的优先级是:Log4j(4念four好像比较有意义,大概是

Logger For Java的意思,我听有的人年Log si J,感觉很别扭,呵呵),Java 1.4 Logging API,Simple Logging。功能是依次减弱。

建议在写Action 的execute()或被execute()调用的业务方法中使用Commons Logging 来代替System.out.println()--当要你把成

百上千的System.out.println()去掉的时候你就会觉得Commons Logging是个多好的东东了。它的用法是:

import org.apache.commons.logging.Log;

import org.apache.commons.logging.LogFactory;

private/protected static Log log = LogFactory.getLog(DispatchAction.class);

如果你用的是DispatchAction,那你就不要自己定义Log的实例了,因为它已经有一个protected的Log实例,直接使用即可。

使用方法是:

if (log.isInfoEnabled()) {

log.Info("some information.");

}

Logging把消息分为6种级别,debug,error,fatal,info,trace,warn。比如,你想记录一条消息,它只是为了给用户一个警告,则可

以使用warn。为什么在每个log.Info()前做一次判断呢?难道如果log级别不允许Info,log.Info()仍然能Info吗?当然不是。它的作用是提

高效率。

比如有个消息是计算前一万个自然数的和(这种消息可能少见)。用直接log.Info()

int sum=0;

for(int i=0;i<10000;i++){

sum+=i;

}

log.Info("the sum of form 1 to 10000 is : "_sum);

如果log.Info是不允许的,那求10000个数的和就白求的。当然如果你的计算机很快或和高斯一样聪明,直接log.Info()也每什么

问题。

闲话少说,回到180多行的BeanUtils.setProperty()方法。这个方法先是处理nested属性,也就是xxx.xxx的请求参数。我们只看看

处理简单属性的必须过程。下面这端代码有点长,但它只做了一件事:将字符串的请求参数转成ActionForm的类型。比如:你在ActionForm里

有个Integer userAge;然后HTTP请求参数里可能会有http://localhost:8080/xxx.do?userAge=21。传人的是字符串,目标是专程Integer。

首先它当然会根据userAge这个字符串查找相应的ActionForm,如果这个ActionForm有个属性也叫userAge,然后就会把这个userAge的类型存

到type里,type的定义是:Class type = null; 得到type的代码很长,这是因为要它考虑很多情况,例如DynaActionForm。

// Convert the specified value to the required type

Object newValue = null;

if (type.isArray() && (index < 0)) { // Scalar value into array

if (value == null) {

String values[] = new String[1];

values[0] = (String) value;

newValue = ConvertUtils.convert((String[]) values, type);

} else if (value instanceof String) {

String values[] = new String[1];

values[0] = (String) value;

newValue = ConvertUtils.convert((String[]) values, type);

} else if (value instanceof String[]) {

newValue = ConvertUtils.convert((String[]) value, type);

} else {

newValue = value;

}

} else if (type.isArray()) { // Indexed value into array

if (value instanceof String) {

newValue = ConvertUtils.convert((String) value,

type.getComponentType());

} else if (value instanceof String[]) {

newValue = ConvertUtils.convert(((String[]) value)[0],

type.getComponentType());

} else {

newValue = value;

}

} else { // Value into scalar

if ((value instanceof String) || (value == null)) {

newValue = ConvertUtils.convert((String) value, type);

} else if (value instanceof String[]) {

newValue = ConvertUtils.convert(((String[]) value)[0],

type);

} else if (ConvertUtils.lookup(value.getClass()) != null) {

newValue = ConvertUtils.convert(value.toString(), type);// Here is my program's break point

} else {

newValue = value;

}

}

最后是:调用PropertyUtils的一些方法设置值。下面代码的第一种情况是有索引的,即你在请求参数里传了field[0]=123之类的

参数,第二种是Map类型的,传的是map(key)=value之类的参数,最一般的就是调用第三个方法。

if (index >= 0) {

PropertyUtils.setIndexedProperty(target, propName,

index, newValue);

} else if (key != null) {

PropertyUtils.setMappedProperty(target, propName,

key, newValue);

} else {

PropertyUtils.setProperty(target, propName, newValue);

}

当然还可以在跟踪下去,不过和这个主题没什么关系了。大概的流程是:

setProperty()方法再调用setNestedProperty()方法(还是代理),在调用setSimpleProperty(),最后通过java.lang.reflect

包调用你在ActionForm里写的setXXX()方法,如setUserAge(Integer userAge)等。

现在说说为什么不能populate java.util.Date类型的数据。关键是ConvertUtils.convert(),即上文有注释的地方。如果这个方

法返回的是一个java.util.Date类型的对象,当然后面都不会有问题。但我发现实际运行的结果是,newValue还是String类型的,因此在后面

通过reflection调用setXXX时出错。

你或许会奇怪ConvertUtils包竟然连java.util.Date都不支持,我也觉得不可思异。我还以为是我使用的不对,然后进入这个类

一看,确实是不支持:

/**

* <p>Utility methods for converting String scalar values to objects of the

* specified Class, String arrays to arrays of the specified Class. The

* actual {@link Converter} instance to be used can be registered for each

* possible destination Class. Unless you override them, standard

* {@link Converter} instances are provided for all of the following

* destination Classes:</p>

* <ul>

* <li>java.lang.BigDecimal</li>

* <li>java.lang.BigInteger</li>

* <li>boolean and java.lang.Boolean</li>

* <li>byte and java.lang.Byte</li>

* <li>char and java.lang.Character</li>

* <li>java.lang.Class</li>

* <li>double and java.lang.Double</li>

* <li>float and java.lang.Float</li>

* <li>int and java.lang.Integer</li>

* <li>long and java.lang.Long</li>

* <li>short and java.lang.Short</li>

* <li>java.lang.String</li>

* <li>java.sql.Date</li>

* <li>java.sql.Time</li>

* <li>java.sql.Timestamp</li>

* </ul>

*

* <p>For backwards compatibility, the standard Converters for primitive

* types (and the corresponding wrapper classes) return a defined

* default value when a conversion error occurs. If you prefer to have a

* {@link ConversionException} thrown instead, replace the standard Converter

* instances with instances created with the zero-arguments constructor. For

* example, to cause the Converters for integers to throw an exception on

* conversion errors, you could do this:</p>

* <pre>

* // No-args constructor gets the version that throws exceptions

* Converter myConverter =

* new org.apache.commons.beanutils.converter.IntegerConverter();

* ConvertUtils.register(myConverter, Integer.TYPE); // Native type

* ConvertUtils.register(myConverter, Integer.class); // Wrapper class

* </pre>

*

* @author Craig R. McClanahan

* @author Ralph Schaer

* @author Chris Audley

* @version $Revision: 1.12 $ $Date: 2003/01/15 21:59:38 $

*/

另外,会不会即时是字符串的,org.apache.commons.beanutils.PropertyUtils.setProperty()也有能力处理呢?

于是又写了个小程序测试。

public class SetSimplePropertyTest {

public SetSimplePropertyTest() {

}

public static void main(String[] args) {

SetSimplePropertyTest setSimplePropertyTest1 = new SetSimplePropertyTest();

String dateStr="2004-01-01 19:00:00";

test.DataBean dataBean=new DataBean();

try {

org.apache.commons.beanutils.PropertyUtils.setProperty(dataBean,

"receiveTime", dateStr);

}

catch (Exception e){

e.printStackTrace();

}

System.out.println(dataBean.getReceiveTime().toString());

}

运行是抛出异常,证明处理不了。

问题找到了,那该怎么解决呢?当然最简单的方法就是使用ConvertUtils能转的java.sql.DateTime等,比较复杂一点的方法就是

自己写一个ConvertUtils。当然,如果你把日前存成String,那更没问题,但如果要将它存入数据库,还得转。尤其在使用DAO模式时,我们

可能用BeanUtils.CopyProperties()方法实现将一个ActionForm拷贝到一个DTO(or VO)对象中时会很麻烦。

还有一个比较好的方法是,属性定义成java.util.Date,但为Struts提高另一个getter/setter方法。这种方法是在middlegen自动

生成的JSP页面看到的。

例如:

private java.util.Date saveDate;

//普通的set/get方法

public void setSaveDate(java.util.Date saveDate){

this.saveDate=saveDate;

}

public java.util.Date getSaveDate(){

return this.saveDate;

}

//为Struts准备的方法,时期的格式假定是 2000-12-31 23:59:59

public void setSaveDateAsString(String saveDate){

java.text.DateFormat dateFormat =new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

this.saveDate=dateFormat.parse(saveDate);

}

public String getSaveDateAsString(){

java.text.DateFormat dateFormat =new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

return dateFormat.Format(this.saveDate);

}

然后在JSP中使用:

<html:form action="xxx.do">

<html:text property="saveDateAsString"/>

</html:form>

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有