A a = new A();
B b = new B();
BeanUtils.copyProperties(a, b);
两个类很简单:
public class A {
private Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
public class B {
private Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
执行的结果是a和id变成了0。这是你预期的结果吗?
为什么会这样呢?
我们来看下copyProperties的源码
/**
* Copy property values from the origin bean to the destination bean
* for all cases where the property names are the same.
* 从来源到目标的复制属性值
*
* For more details see BeanUtilsBean
.
* 详情查看BeanUtilsBean
*
*/
public static void copyProperties(Object dest, Object orig)
throws IllegalAccessException, InvocationTargetException {
BeanUtilsBean.getInstance().copyProperties(dest, orig);
}
底层是调用BeanUtilsBean进行转换:
普通Java转换的方法如下
// 获取属性描述
PropertyDescriptor[] origDescriptors =
getPropertyUtils().getPropertyDescriptors(orig);
for (int i = 0; i < origDescriptors.length; i++) {
String name = origDescriptors[i].getName();
if ("class".equals(name)) {
continue; // No point in trying to set an object's class
}
// 查询属性是否可以设值
if (getPropertyUtils().isReadable(orig, name) &&
getPropertyUtils().isWriteable(dest, name)) {
try {
// 获取来源单bean该属性的值
Object value =
getPropertyUtils().getSimpleProperty(orig, name);
// 如果没有值的话就在这里处理
copyProperty(dest, name, value);
} catch (NoSuchMethodException e) {
// Should not happen
}
}
}
如果来源对象的属性值为null的话就会走到copyProperty(dest, name, value);
protected Object convert(Object value, Class type) {
Converter converter = getConvertUtils().lookup(type);
if (converter != null) {
log.trace(" USING CONVERTER " + converter);
return converter.convert(type, value);
} else {
return value;
}
}
getConvertUtils()会根据对象拿到对象类型的Convert接口,然后来进行转换。
这里就是会拿到LongConvert实现,其源码如下:
@Override
public Object convert(Object source, Class clz) {
// 这里是关键
Long l = new Long(0);
if(source == null) {
return getDefaultValue(l, clz);
}
if(source instanceof Long) {
l = (Long) source;
} else if(source instanceof Short) {
l = new Long((Short)source);
} else if(source instanceof Integer) {
l = new Long((Integer)source);
} else if(source instanceof Double) {
l = ((Double) source).longValue();
} else if(source instanceof Float) {
l = ((Float)source).longValue();
} else if(source instanceof String) {
try {
l = new Long(Long.parseLong((String) source));
} catch (Exception e) {
l = getDefaultValue(l, clz);
}
}
return l;
}
可以发现其在convert的时候会设置默认值.
这个问题非常隐蔽,我不清楚为什么apache要这么设计,在我这种场景下,是不希望对象没设置默认值的。
这个问题出现在依赖的commons-beanutils 1.8.3版本中。
我换成1.9.3得到解决。
还有一个新的坑,对于spring的beanUtils复制,如果对象的属性是继承并且重定义了属性,反射会copy不过去。