zoukankan      html  css  js  c++  java
  • 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)

  • 相关阅读:
    React Hooks用法大全
    SourceTree3.2.6版本跳过注册办法
    微服务SpringCloud项目架构搭建入门
    参考微信公众平台的加解密接口实现方式
    带有function的JSON对象的序列化与还原
    关于datatables与jquerUI版本冲突问题
    有关于分布式缓存Hazelcast
    bootstrap datepicker含有hasDatepicker无法弹出
    SpringMVC学习系列-后记 解决GET请求时中文乱码的问题
    Spring boot整合Hive
  • 原文地址:https://www.cnblogs.com/xiaozhuyuan/p/7382465.html
Copyright © 2011-2022 走看看