记 Struts2 Date类型转换的坑 java.lang.NoSuchMethodException: xxxAction.setXXXDate([Ljava.lang.String;)

我觉得很有必要把这两天的心路历程记录下来。

场景描述:

1. 这是一个两年前就做好的功能,没有用户报过故障;
2. 用户报自己的 iPhone5S(iOS 7.1.2) 查询不了数据,但从别的入口进去可以查到。重启程序依然如故。前两天还正常,没有进行系统升级;
3. 别的用户使用 Samsung Note 3 (Android 4.3) 正常;
4. 不能访问生产日志。

尝试步骤:

1. 使用 小米2(Android 4.4.4)、iPhone4S(iOS 8.1.3)、iPhone6(iOS 8.1.3),向用户要了账号,使用正常;
2. 使用测试环境再测一遍,正常;

我整个人就不好了。。。

不怕Bug,就怕不能重现的Bug,还TM是不能看到日志的Bug!

好吧,淡定,反正故障即使今天解决了也上不了线 ← ← ,先 debug 看看本机日志输出什么。

只有 iPhone 上出现了这个问题,好,就先来debug下 iPhone,Xcode 走起。

Build,Run,点一下,哎呦我擦,竟然重现了!再点几下,百分百重现!看到希望了!(因为我一般都是真机调试的,之前模拟器上只是大概调下,没发现这个情况)

看看日志,没有异常输出。因为这是个使用 Cordova 进行 原生和 Javascript 混编的应用,默认在Xcode控制台的日志输出都是原生代码的,js 的console.log不会在这里输出,之前调试也是电脑上调好页面再放模拟器跑的,没有做过 js 在 Xcode 的日志输出。Google 了下,比较简单的方法是开启电脑中 Safari的 开发菜单,在偏好设置 -> 高级 中打钩即可。然后跑模拟器,在电脑上 Safari 的 开发菜单里即可启用类似 Chrome 的控制台。

恩,照着做了,结果Safari控制台的输出是正常的,该传的参数都是给对了的,但是网络请求竟然没有显示出来,查了下,网上别人的是有的,不过人用的是真机,不知是不是和这有关,先不管,祭出 Charles。

结果 Charles 也没输出模拟器的请求记录,光显示我电脑上的了。查了下,要点击 Help, 安装 SSL 证书,重启模拟器才行。

恩,总算把网络请求输出来了,一看,请求地址没问题啊,参数也很对啊,但就是返回值是错的,奇怪。

然后,我把请求链接复制了,贴到电脑 Chrome 上访问,返回值是正确的啊!难道是浏览器的问题,Safari 平时用起来感觉也是有 bug。于是,链接贴到电脑 Safari,回车,然后。。。返回也是对的啊!

接着我把链接贴到模拟器的 Safari 中,返回值是错的。

这显然是跟 iPhone 的机器本身有关系啊!继续debug,模拟器选择和我之前测的两台真机 iPhone4S、iPhone6 一样,系统版本也选择一样。结果是,模拟器的返回值还是错的。。。

一口老血,这是为啥!

想了老久,想不明白,想想就后台没看了,去看看测试上后台的日志有没收获吧,虽然这个问题看起来跟后台没半毛钱关系 - -

抱着试一试的态度,看后台日志,结果,有出错日志了!赶紧看看! java.lang.NoSuchMethodException: xxxAction.setXXXDate([Ljava.lang.String;)

没有把某个参数的 String 转为 Date 的方法。

确实有这个 Date 类型的属性,但是Struts 会自动转换才对。问题应该就出在这了。Google 下,这篇文章讲得挺详细的。

问题原因在于,Struts2 会对七种时间格式进行自动转换,其中还硬编码了”yyyy-MM-dd’T’HH:mm:ss” 这么一种格式,这是美国语言中使用的日期格式。我的请求参数中,有个日期参数是 yyyy-MM-dd 格式的,而对于yyyy-MM-dd这种日期类型,在英语语言中是没法匹配的,由于Struts2匹配日期时,使用了Locale,因此,如果客户端默认的语言环境是英语,则 Struts 无法匹配需转换的日期格式,自动转换就失败了。用户的机器和我的模拟器的语言环境都是英语,于是日期参数没有转换成功,请求就失败了。

明白了原因,立马把模拟器的环境改为中文,把真机的环境改为英文,结果如想象中,模拟器有数据了,真机没有。

真相大白。

解决方法

问题出在后台上,还得后台改。于是,创建一个继承自 DefaultTypeConverter 的日期转换工具类,在 struts.xml 的同级目录创建 xwork-conversion.properties, 内容为 java.util.Date=工具类完整限定类名,表示 Struts的 Date 类型转换由这个工具类来进行。

至此,问题解决。

(P.S. 在创建日期转换工具类的时候发现,原来在工程里已经有这样一个类了,但 TMD 说好的转换的配置文件呢!为什么没有!!!而且这个类引入了一个定义日期格式的配置文件,尼玛也没有!!!心中一万头草泥马在奔腾!!!我在填史前坑的道路上已经越走越远。。。)