Apache common-utils的BeanUtils,Spring的BeanUtils,以及cglib的BeanCopier复制对象属性的性能对比。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
import org.apache.commons.beanutils.BeanUtils;
import org.springframework.cglib.beans.BeanCopier;
import java.lang.reflect.InvocationTargetException;
**
* 测试apache BeanUtils和cglib的BeanCopier的性能对比。
* @author j.tommy
* @date 2016-09-13 15:20.
*/
public class BeanCopyTest {
    public static void main(String[] args) {
        Teacher t = new Teacher(1,"admin");
        Teacher t2 = new Teacher();
        testSpringBeanUtils(t);
        testCommonUtils(t);
        testCgLib(t);
    }
    public static void testSpringBeanUtils(Teacher teacher) {
        Teacher t = new Teacher();
        Long start = System.currentTimeMillis();
        for (int i=0;i<100*10000;i++) {
            org.springframework.beans.BeanUtils.copyProperties(t, teacher);
        }
        Long end = System.currentTimeMillis();
        System.out.println("Spring time spend:" + (end - start));
    }
    public static void testCommonUtils(Teacher teacher) {
        Teacher t = new Teacher();
        Long start = System.currentTimeMillis();
        for (int i=0;i<100*10000;i++) {
            try {
                BeanUtils.copyProperties(t,teacher);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
        Long end = System.currentTimeMillis();
        System.out.println("Common-utils time spend:" + (end - start));
    }
    public static void testCgLib(Teacher teacher) {
        Teacher copy = new Teacher();
        BeanCopier bc = BeanCopier.create(Teacher.class,Teacher.class,false);
        Long start = System.currentTimeMillis();
        for (int i=0;i<100*10000;i++) {
            bc.copy(teacher,copy,null);
        }
        Long end = System.currentTimeMillis();
        System.out.println("CGlib time spend:" +  (end-start));
    }
    static class Teacher {
        private int id;
        private String name;
        public Teacher() {
        }
        public int getId() {
            return id;
        }
        public void setId(int id) {
            this.id = id;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public Teacher(int id, String name) {
            this.id = id;
            this.name = name;
        }
    }
}

可以看出,common-utils的BeanUtils复制对象属性是最差的,最好的是CGLIB,统计结果都是复制100万次属性。
所以,在大量复制对象属性的场合,最好使用cglib的BeanCopier来做。

使用BeanCopier需要注意的事项:
1)目标类的getter方法不能比setter方法多;
这个是cglib的一个bug,它是读取所有的getter方法,然后调用相关的setter方法去注入属性,所以会触发空指针异常。
这个是在使用BeanCopier的create方法时发生的。
bug可以参考:https://sourceforge.net/p/cglib/bugs/32/

2)使用时最好缓存BeanCopier,而不是每次使用的时候创建一个。具体类说就是使用一个静态的变量,而不是一个类成员变量。