问题现象
最近公司内部一个运行已久的产品,在最近一次上线后操作出现了“No value specified for 'Date'”的错误,异常堆栈如下:
Caused by: org.apache.commons.beanutils.ConversionException: No value specified for 'Date'
at org.apache.commons.beanutils.converters.AbstractConverter.handleMissing(AbstractConverter.java:310)
at org.apache.commons.beanutils.converters.AbstractConverter.convert(AbstractConverter.java:136)
at org.apache.commons.beanutils.converters.ConverterFacade.convert(ConverterFacade.java:60)
at org.apache.commons.beanutils.BeanUtilsBean.convert(BeanUtilsBean.java:1078)
at org.apache.commons.beanutils.BeanUtilsBean.copyProperty(BeanUtilsBean.java:437)
at org.apache.commons.beanutils.BeanUtilsBean.copyProperties(BeanUtilsBean.java:286)
at org.apache.commons.beanutils.BeanUtils.copyProperties(BeanUtils.java:137)
通过修改记录并没有发现关于BeanUtils以及源目标模型的修改。
问题分析
通过异常堆栈和BeanUtils的copyProperties
源码分析来看,问题的根源是要copy的源模型中的存在“Date”类型的属性,但其值为null。查看源模型类源码的修改记录和数据库中的字段和值,发现近期均未变化,也就是说在本次上线前,这种情况的源模型是可以使用BeanUtils来copy属性的。
百度一下,大多数给出的解决方案都是在使用BeanUtils的copyProperties
之前注册一个DateConverter:
// 增加注册DateConverter代码,设置默认值为null。
ConvertUtils.register(new DateConverter(null), java.util.Date.class);
BeanUtils.copyProperties(...);
增加这句代码可以解决这个问题,但是我们上线之前没有这句代码也是可用的,换google检索一下,最终在StackOverflow上查到了问题的根本原因,这个问题是beanutils 1.8.x版本上才会出现,由于beanutils 1.8.x版本上修改了这个copyProperties
方法,如果要转换的类型是Date、Calendar或其他一些类型时,当值为null且没有配置默认值时会抛出异常。1.9.0及之后的版本修复了这个问题。
查看我们上线之前服务器上的beanutils的版本为1.9.3,而本次上线包中的beanutils的版本是1.8.3,显然是打包时某些依赖间接依赖了低版本的beanutils进而影响了打包结果。
问题原因
apache的BeanUtils 1.8.x版本的copyProperties
修改了Date类型的null值的处理逻辑,改为了无默认值值抛出异常。1.9.0之后的版本修复了该问题。
beanutils关于该bug的记录:https://issues.apache.org/jira/browse/BEANUTILS-454
解决办法
以下几种方案选其一即可。
pom.xml中固化使用的beanutils的版本
在pom.xml中使用dependency:tree检查依赖的beanutils的版本,可以通过强制指定beanutils依赖的方式来固定使用的beanutils的版本。
增加DateConverter转换器(百度上的通用解决方案)
在程序的初始化代码中增加DateConverter转换器类:
// 增加注册DateConverter代码,设置默认值为null。
ConvertUtils.register(new DateConverter(null), java.util.Date.class);
使用java.sql.Date取代java.util.Date(未验证)
模型中日期属性的类型使用java.sql.Date
而不是java.util.Date
。
使用spring的BeanUtils工具类(未验证)
spring中提供了一个org.springframework.beans.BeanUtils
工具类,跟apache的BeanUtils功能差不多一样。
注意:如果之前使用的是apache的BeanUitls,不建议采用该方法。一是改动量太大,二是spring的BeanUitls一样存在各种各样的坑。
参考资料
- How to ask BeanUtils to ignore null values
- copyProperties() throws conversion exception for null Date