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的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()))

我们解决方法是, 修改了这几个属性的参数赋值方式.
最终说一下经验教训吧:

  1. 代码规范问题, bean属性类型要严格按数据字典中类型来定义
  2. 升级类库要做充分的回归测试

猜你喜欢

转载自my.oschina.net/u/1404252/blog/1627348