commons.beanutils转换Date类型值为null的字段时报错,ConversionException: No value specified for 'Date'
错误发生:用户自定义了一个将Map转成指定Bean类的转换器,继承自org.springframework.beans.BeanUtils ,如下
public static <T> T convertMap(Map<String, Object> sources, Class<T> type) throws IllegalAccessException, InvocationTargetException { T bean = BeanUtils.instantiate(type); org.apache.commons.beanutils.BeanUtils.populate(bean, sources); return bean; }
错误信息:
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.setProperty(BeanUtilsBean.java:1011)
at org.apache.commons.beanutils.BeanUtilsBean.populate(BeanUtilsBean.java:830)
at org.apache.commons.beanutils.BeanUtils.populate(BeanUtils.java:433)
跟踪发现错误发生在hannleMissing处理空值的函数中。
关键代码:
public abstract class AbstractConverter implements Converter { .................................此处省略代码.................................................... protected Object handleMissing(Class type) { if (useDefault || type.equals(String.class)) { Object value = getDefault(type); if (useDefault && value != null && !(type.equals(value.getClass()))) { try { value = convertToType(type, defaultValue); } catch (Throwable t) { log().error(" Default conversion to " + toString(type) + "failed: " + t); } } if (log().isDebugEnabled()) { log().debug(" Using default " + (value == null ? "" : toString(value.getClass()) + " ") + "value '" + defaultValue + "'"); } return value; } ConversionException cex = new ConversionException("No value specified for '" + toString(type) + "'"); if (log().isDebugEnabled()) { log().debug(" Throwing ConversionException: " + cex.getMessage()); log().debug(" " + DEFAULT_CONFIG_MSG); } throw cex; } protected void setDefaultValue(Object defaultValue) { useDefault = false; if (log().isDebugEnabled()) { log().debug("Setting default value: " + defaultValue); } if (defaultValue == null) { this.defaultValue = null; } else { this.defaultValue = convert(getDefaultType(), defaultValue); } useDefault = true; } .....................此处省略代码....................................... }
上面代码红色部分是具体报错位置,绿色部分是用 new XXConverter(null或defaultval) 注册默认值后执行这个函数,后面对空值的数据转换可以设置为注册时的默认值。
报错原因:本项目中Date转换器没有注册到默认值。
public ConvertUtilsBean() { converters.setFast(false); deregister(); converters.setFast(true); } public void deregister() { converters.clear(); registerPrimitives(false); registerStandard(false, false); registerOther(true); registerArrays(false, 0); register(BigDecimal.class, new BigDecimalConverter()); register(BigInteger.class, new BigIntegerConverter()); } ...........................................此处省略部分代码.................................................... private void registerOther(boolean throwException) { register(Class.class, throwException ? new ClassConverter() : new ClassConverter(null)); register(java.util.Date.class, throwException ? new DateConverter() : new DateConverter(null)); register(Calendar.class, throwException ? new CalendarConverter() : new CalendarConverter(null)); register(File.class, throwException ? new FileConverter() : new FileConverter(null)); register(java.sql.Date.class, throwException ? new SqlDateConverter() : new SqlDateConverter(null)); register(java.sql.Time.class, throwException ? new SqlTimeConverter() : new SqlTimeConverter(null)); register(Timestamp.class, throwException ? new SqlTimestampConverter() : new SqlTimestampConverter(null)); register(URL.class, throwException ? new URLConverter() : new URLConverter(null)); }
项目启动后用的deregister函数初始化部分非原始数据类型的转换器,Date类型转换器注册用的是 new DateConverter() ,未设置默认值(上面代码绿色部分)。因此在实际转换日期字段时,由于Date转换器没有设置默认值,useDefault为false,if条件没有进入设值,然后就执行下面的抛出错误了。
解决方法:
方法一:如果Date类型没必要设为null,在调用转换器前就设置对应的日期数据,这个治标不治本。
方法二:可以自定义转换器的类中设置Date转换器null对应的默认值。不同类型的转换器默认值设置,可以参考ConvertUtilsBean类等,如 private Double defaultDouble = new Double(0.0); 还有相关方法 deregister -> registerPrimitives等 ,一般DoubleConverter转换器会讲null值默认转换为0.0。转换器默认值的设置以项目需要为主,数值型类的可以自定义为null或对应的0值。
static { ConvertUtils.register(new LongConverter(null), Long.class); ConvertUtils.register(new ShortConverter(null), Short.class); ConvertUtils.register(new IntegerConverter(null), Integer.class); ConvertUtils.register(new DoubleConverter(null), Double.class); ConvertUtils.register(new BigDecimalConverter(null), BigDecimal.class); ConvertUtils.register(new DateConverter(null), Date.class); }
注:数值类型转换器父类NumberConverter转换方法toNumber(Class sourceType, Class targetType, Number value)