Bean拷贝工具类性能比较

Bean拷贝工具类性能比较

引言

几年前做过一个项目,接入新的api接口。为了和api实现解耦,决定将api返回的实体类在本地也建一个。这样做有两个好处

  1. 可以在api变更字段的时候保持应用稳定性
  2. 可以对返回的实体的属性做处理,以提高可读性。例如接口返回long类型的时间戳,则将该字段在本地实体类中对应字段设置为date类型方便使用。

大致是这样的一个应用场景。当时刚毕业,充斥的都是A.setName(B.getName)这种类型的代码。当字段非常多的时候看起来非常臃肿,最重要的给人感觉不优雅。

再给我一次机会

如果上天再给我一次机会,我会跟他说,我有四种工具类可以优雅的解决这个问题。
分别是

  • org.apache.commons.beanutils.PropertyUtils.copyProperties
  • org.apache.commons.beanutils.BeanUtils.copyProperties
  • org.springframework.beans.BeanUtils.copyProperties
  • net.sf.cglib.beans.BeanCopier

Talk is cheap, show me code

定义SourceBean

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
package com.zhaoyangwoo.model;
/**
* Created by john on 16/7/28.
*/
public class SourceBean {
private Integer age;
private String title;
private String source;
private Boolean eat;
public SourceBean(Integer i, String name, Boolean eat, String source) {
this.age = i;
this.title = name;
this.eat = eat;
this.source = source;
}
public void setAge(Integer age) {
this.age = age;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Boolean getEat() {
return eat;
}
public void setEat(Boolean eat) {
this.eat = eat;
}
public String getSource() {
return source;
}
public void setSource(String source) {
this.source = source;
}
public Integer getAge() {
return age;
}
}

定义DesBean

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
package com.zhaoyangwoo.model;
/**
* Created by john on 16/7/28.
*/
public class DstBean {
private Integer age;
private String title;
private Boolean eat;
private String self;
public String getSelf() {
return self;
}
public void setSelf(String self) {
this.self = self;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Boolean getEat() {
return eat;
}
public void setEat(Boolean eat) {
this.eat = eat;
}
}

测试函数

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
package com.zhaoyangwoo.biz;
import com.zhaoyangwoo.model.DstBean;
import com.zhaoyangwoo.model.SourceBean;
import net.sf.cglib.beans.BeanCopier;
/**
* Created by john on 16/7/28.
*/
public class BeanCopyMain {
private static BeanCopier beanCopier = BeanCopier.create(SourceBean.class, DstBean.class, false);
public static void main(String[] args) throws Exception {
Integer[] times = {10000, 1000, 10};
for (int i = 0; i < times.length; i++) {
Integer time = times[i];
doCopy(time, (x, y) -> org.apache.commons.beanutils.PropertyUtils.copyProperties(y, x), "org.apache.commons.beanutils.PropertyUtils.copyProperties");
doCopy(time, (x, y) -> org.apache.commons.beanutils.BeanUtils.copyProperties(y, x), "org.apache.commons.beanutils.BeanUtils.copyProperties");
doCopy(time, (x, y) -> org.springframework.beans.BeanUtils.copyProperties(y, x), "org.springframework.beans.BeanUtils.copyProperties");
doCopy(time, (x, y) -> beanCopier.copy(x, y, null), "net.sf.cglib.beans.BeanCopier.copy");
doCopy(time, (x, y) -> {
y.setEat(x.getEat());
y.setTitle(x.getTitle());
y.setAge(x.getAge());
}, "getter/setter");
}
}
static void doCopy(Integer time, BeanCopy beanCopy, String type) throws Exception {
long startTime = System.currentTimeMillis();
for (int j = 0; j < time; j++) {
SourceBean sourceBean = new SourceBean(j, "source" + j, false, "abc");
DstBean dstBean = new DstBean();
beanCopy.copy(sourceBean, dstBean);
}
System.out.printf("执行%d次用时%dms---------%s%n", time, System.currentTimeMillis() - startTime, type);
}
interface BeanCopy {
void copy(SourceBean from, DstBean to) throws Exception;
}
}

执行结果如下

结论

根据上面的运行结果,我们可以得出以下结论

  • property少,写起来也不麻烦,就直接用传统的getter/setter,性能最好
  • property多,转换不频繁,那就省点事吧,使用org.apache.commons.beanutils.BeanUtils.copyProperties
  • property多,转换很频繁,为性能考虑,使用net.sf.cglib.beans.BeanCopier.BeanCopier,性能近乎getter/setter。但是BeanCopier的创建时消耗较大,所以不要频繁创建该实体,最好的处理方式是静态化或者缓存起来。

作者: wuzhaoyang(John)
出处: http://wuzhaoyang.me/
因为作者水平有限,无法保证每句话都是对的,但能保证不复制粘贴,每句话经过推敲。希望能表达自己对于技术的态度,做一名优秀的软件工程师。