近期把一个老项目的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()))
我们解决方法是, 修改了这几个属性的参数赋值方式.
最终说一下经验教训吧:
- 代码规范问题, bean属性类型要严格按数据字典中类型来定义
- 升级类库要做充分的回归测试