Troubleshooting BeanUtils.copyProperties

Keywords: Spring Attribute

Recently, the spring version of an old project was upgraded from 3.1.1.RELEASE to 3.2.18.RELEASE. As a result, a lot of problems appeared on the line. It was found that the attribute value of some int type of a bean instance was 0, The value of this bean is to query the interface, and then assign a value through spring's tool class method org.springframework.beans.BeanUtils.copyProperties(Object, Object), while the source bean's property types are long(target bean's property types are int), and the spring version back to 3.1.1.RELEASE problem disappears

After troubleshooting, the BeanUtils of spring start from 3.2.7.RELEASE, and the copyProperties method adds type verification. If the return type of the source property acquisition method is inconsistent with the parameter type of the target property writing method, the assignment is skipped. The copyProperties method property copy code in 3.2.7.RELEASE is as follows:

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);
                }
            }
        }
    }
}
    

The property copy code of copyProperties method in 3.2.6 is as follows:

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. Judgment if (readmethod! = null & & classutils. Isassignable (writemethod. Getparametertypes() [0], readmethod. Getreturntype()))

Our solution is to modify the parameter assignment of these properties
Finally, let's talk about the lessons learned:

  1. For code specification, the bean property type should be defined strictly according to the type in the data dictionary
  2. Full regression test should be done to upgrade the class library

Posted by Sianide on Fri, 03 Apr 2020 16:53:07 -0700