BeanUtils.copyProperties問題排查

近期把一個老項目的spring版本從3.1.1.RELEASE升級到了3.2.18.RELEASE, 結果線上出現了一堆問題, 經排查是一個bean實例某幾個int類型屬性值爲0致使的, 這個bean的值是查詢接口, 而後經過spring的工具類方法org.springframework.beans.BeanUtils.copyProperties(Object, Object)賦值, 而source bean的這幾個屬性類型爲long(target bean這幾個屬性類型爲int), 把spring版本回退到3.1.1.RELEASE問題消失.spring

經排查, spring的BeanUtils從3.2.7.RELEASE開始, copyProperties方法加了類型校驗, source的屬性獲取方法返回類型和target的屬性寫入方法的參數類型不一致就跳過賦值, 3.2.7.RELEASE中copyProperties方法屬性copy代碼以下:工具

for (PropertyDescriptor targetPd : targetPds) {
    Method writeMethod = targetPd.getWriteMethod();
    if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
        PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
        if (sourcePd != null) {
            Method readMethod = sourcePd.getReadMethod();
            if (readMethod != null &&
                    ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
                try {
                    if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
                        readMethod.setAccessible(true);
                    }
                    Object value = readMethod.invoke(source);
                    if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
                        writeMethod.setAccessible(true);
                    }
                    writeMethod.invoke(target, value);
                }
                catch (Throwable ex) {
                    throw new FatalBeanException(
                            "Could not copy property '" + targetPd.getName() + "' from source to target", ex);
                }
            }
        }
    }
}

3.2.6中copyProperties方法屬性copy代碼以下:測試

for (PropertyDescriptor targetPd : targetPds) {
    if (targetPd.getWriteMethod() != null &&
            (ignoreProperties == null || (!ignoreList.contains(targetPd.getName())))) {
        PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
        if (sourcePd != null && sourcePd.getReadMethod() != null) {
            try {
                Method readMethod = sourcePd.getReadMethod();
                if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
                    readMethod.setAccessible(true);
                }
                Object value = readMethod.invoke(source);
                Method writeMethod = targetPd.getWriteMethod();
                if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
                    writeMethod.setAccessible(true);
                }
                writeMethod.invoke(target, value);
            }
            catch (Throwable ex) {
                throw new FatalBeanException("Could not copy properties from source to target", ex);
            }
        }
    }
}

3.2.7.RELEASE中添加了判斷if (readMethod != null && ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType()))代碼規範

咱們解決方法是, 修改了這幾個屬性的參數賦值方式.
最終說一下經驗教訓吧:code

  1. 代碼規範問題, bean屬性類型要嚴格按數據字典中類型來定義
  2. 升級類庫要作充分的迴歸測試
相關文章
相關標籤/搜索