Fork me on GitHub

Java 开发中的对象拷贝

前言

在 Java 开发中,很多时候需要将两个属性基本相同的对象进行属性复制,比如 DO 转 VO等等。

本文主要介绍自己实现的简易拷贝工具类与 Spring 提供的属性拷贝的对比。

Spring 提供的属性拷贝

在 Spring 中直接调用 BeanUtils.copyProperties();即可。

它的核心通过循环 target 的所有方法名,然后在 source 中找到对应的方法名,最后通过反射从 source 中获取并写入 target 中。

Spring 没有通过 java.lang.reflect 中的 Field 来做,而是通过 java.beans 中的 PropertyDescriptor 来实现。

  补充:PropertyDescriptor 描述 Java Bean 通过一对存储器方法导出的一个属性。

通过 PropertyDescriptor 提供的 getReadMethod() 和 getWriteMethod() 方法,可以方便的获取到读取、写入属性值的方法(Method)。

同时,Spring 也做了缓存,在测试中,第一次的对象拷贝用时 300+ 毫秒,之后在缓存中获取,用时 0 毫秒。

源码如下图所示:

缓存源码:

自己写的简易版

 1 public static void copyBean(Object target, Object source) throws IllegalAccessException, InstantiationException, NoSuchFieldException {
 2         Class targetClass = target.getClass();
 3         Class sourceClass = source.getClass();
 4         // 获取目标类的所有参数 Field
 5         Field[] fields = targetClass.getDeclaredFields();
 6         // 为目标类的每个参数设值
 7         for (Field field : fields) {
 8             // 如果数据源对象中存在对应的参数
 9             Field sourceField = sourceClass.getDeclaredField(field.getName());
10             if (null != sourceField) {
11                 Field targetField = targetClass.getDeclaredField(field.getName());
12                 targetField.setAccessible(true);
13                 sourceField.setAccessible(true);
14                 targetField.set(target, sourceField.get(source));
15             }
16         }
17     }
18 
19     // 类似 Spring 的版本
20     public static void copyBeanByMethod(Object target, Object source) throws IntrospectionException, InvocationTargetException, IllegalAccessException {
21         Class targetClass = target.getClass();
22         Class sourceClass = source.getClass();
23         Field[] sourceFields = sourceClass.getDeclaredFields();
24         for (Field field : sourceFields) {
25             PropertyDescriptor targetProperty;
26             try {
27                 targetProperty = new PropertyDescriptor(field.getName(), targetClass);
28             } catch (IntrospectionException e) {
29                 continue;
30             }
31             Method writeMethod = targetProperty.getWriteMethod();
32             if (writeMethod != null) {
33                 PropertyDescriptor sourceProperty = new PropertyDescriptor(field.getName(), sourceClass);
34                 Method readMethod = sourceProperty.getReadMethod();
35                 if (!Modifier.isPublic(readMethod.getModifiers())) {
36                     readMethod.setAccessible(true);
37                 }
38                 // 读取 source 中属性的值
39                 Object value = readMethod.invoke(source);
40                 if (!Modifier.isPublic(writeMethod.getModifiers())) {
41                     writeMethod.setAccessible(true);
42                 }
43                 // 为 target 对应的属性赋值
44                 writeMethod.invoke(target, value);
45             }
46         }
47     }

小结

Spring 所提供的属性拷贝虽然第一次效率较低,但随后如果再次使用相同的 source 进行拷贝,则 Spring 会通过第一次拷贝保存的缓存来直接进行快速的拷贝

参考资料

[1] 谈谈 Java 开发中的对象拷贝

posted @ 2017-08-20 22:15  郑斌blog  阅读(534)  评论(0编辑  收藏  举报