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)

posted @ 2017-08-17 16:28  筱悦  阅读(11851)  评论(0编辑  收藏  举报