錯誤發生:用戶自定義了一個將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)