总结关于BeanUtils拷贝对象遇到的坑
实际开发中经常会用到的BeanUtils的一些方法,比如copyProperties拷贝对象中的属性等,楼主在使用的过程中也遇到了一些坑,总结一下,这里我用到的是Spring的BeanUtils,后面会介绍几种BeanUtils的一些效率相关的问题。
话不多说,首先创建两个对象:
@Getter
@Setter
@Accessors(chain = true)
public class Test1 {
private Long id;
private String name;
private String sex;
}
public class Test2 {
private Long id;
private String name;
private String sex;
}
public static void main(String[] args) {
Test1 test1 = new Test1()
.setId(1L)
.setName("张三")
.setSex("男");
Test2 test2 = new Test2();
BeanUtils.copyProperties(test1, test2);
System.err.printf(JSON.toJSONString(test2));
}
按道理说这样就可以将test1中的属性值拷贝到Test2中,但是并没有理所应当的拷贝成功,不服输的我立马点进源码看看调用copyProperties的时候到底做了什么。
private static void copyProperties(Object source, Object target, @Nullable Class<?> editable,
@Nullable String... ignoreProperties) throws BeansException {
Assert.notNull(source, "Source must not be null");
Assert.notNull(target, "Target must not be null");
Class<?> actualEditable = target.getClass();
if (editable != null) {
if (!editable.isInstance(target)) {
throw new IllegalArgumentException("Target class [" + target.getClass().getName() +
"] not assignable to Editable class [" + editable.getName() + "]");
}
actualEditable = editable;
}
PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);
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);
}
}
}
}
}
}
看完源码之后才发现当调用此方法时是调用了Test2中的Getter和Setter方法。于是我给Test2中加入了Getter和Setter方法。
@Getter
@Setter
public class Test2 {
private Long id;
private String name;
private String sex;
}
此时拷贝成功!
再来介绍另一个坑,如果存在属性相同的内部类,但是属性不是在同一个类中,则调用copyProperties时不会拷贝属性值。
@Getter
@Setter
@Accessors(chain = true)
public class Test3 extends Test2 {
private Long id;
private String name;
private String sex;
}
还是以上述创建的Test1和Test2两个类为例,如果要拷贝Test3这样的一个类,因为上述创建Test2时候同样也有这三个字段,此时有三对重复的字段,这样在调用拷贝对象的方法是是不会拷贝的。
https://segmentfault.com/a/1190000022021450/分享这篇文章链接详细介绍了几种BeanUtils的性能问题,大家可以参考参考大牛的测试,包括现在属性拷贝的新工具MapStruct,使用了一次感觉很强大,后续写关于MapStruct的专栏。