Bean拷贝性能对比
这里主要对比这四常用的工具(Apache BeanUtils、PropertyUtils、Spring BeanUtils、Cglib BeanCopier),其实还有一个增强的工具类hutool的copy也是对Cglib的增强
背景:性能测试时,发现CPU占有率很高,经过jprofiler的分析,发现CPU大部分被BeanUtils占用了,成了性能瓶颈。
啥也不多说 上代码和数据说话
1.依赖
<!-- cglib依赖 -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.10</version>
</dependency>
<!-- commons-beanutils依赖 -->
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.4</version>
</dependency>
<!-- 验证码依赖 -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.0</version>
</dependency>
<!-- org.springframework.beans.BeanUtils依赖 springboot直接引用spring-boot-starter-web就可以了 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.2.7.RELEASE</version>
<scope>compile</scope>
</dependency>
2.代码
2.1两个实体 一个是原对象(FromBean)个是目标对象(ToBean)
package com.jxj.seckill.testcopy;
import lombok.Data;
@Data
public class FromBean {
private String name;
private int age;
private String address;
private String idno;
private double money;
}
package com.jxj.seckill.testcopy;
import lombok.Data;
@Data
public class ToBean {
private String name;
private int age;
private String address;
private String idno;
private double money;
}
2.2拷贝业务
package com.jxj.seckill.testcopy;
public class BenchmarkTest {
private int count;
public BenchmarkTest(int count) {
this.count = count;
System.out.println("性能测试" + this.count + "==================");
}
public void benchmark(IMethodCallBack m, FromBean frombean) {
try {
long begin = new java.util.Date().getTime();
ToBean tobean = null;
System.out.println(m.getMethodName() + "开始进行测试");
for (int i = 0; i < count; i++) {
tobean = m.callMethod(frombean);
}
long end = new java.util.Date().getTime();
System.out.println(m.getMethodName() + "耗时" + (end - begin));
System.out.println(tobean.getAddress());
System.out.println(tobean.getAge());
System.out.println(tobean.getIdno());
System.out.println(tobean.getMoney());
System.out.println(tobean.getName());
System.out.println(" ");
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.3测试主体
package com.jxj.seckill.testcopy;
import net.sf.cglib.beans.BeanCopier;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;
public class TestMain {
/**
* @param args
*/
public static void main(String[] args) {
FromBean fb = new FromBean();
fb.setAddress("浙江杭州");
fb.setAge(18);
fb.setMoney(99.99);
fb.setIdno("4115261999125512152465");
fb.setName("江先进");
IMethodCallBack beanutilCB = new IMethodCallBack() {
@Override
public String getMethodName() {
return "BeanUtil.copyProperties";
}
@Override
public ToBean callMethod(FromBean frombean) throws Exception {
ToBean toBean = new ToBean();
BeanUtils.copyProperties(toBean, frombean);
return toBean;
}
};
IMethodCallBack propertyCB = new IMethodCallBack() {
@Override
public String getMethodName() {
return "PropertyUtils.copyProperties";
}
@Override
public ToBean callMethod(FromBean frombean) throws Exception {
ToBean toBean = new ToBean();
PropertyUtils.copyProperties(toBean, frombean);
return toBean;
}
};
IMethodCallBack springCB = new IMethodCallBack() {
@Override
public String getMethodName() {
return "org.springframework.beans.BeanUtils.copyProperties";
}
@Override
public ToBean callMethod(FromBean frombean) throws Exception {
ToBean toBean = new ToBean();
org.springframework.beans.BeanUtils.copyProperties(frombean,
toBean);
return toBean;
}
};
IMethodCallBack cglibCB = new IMethodCallBack() {
BeanCopier bc = BeanCopier.create(FromBean.class, ToBean.class, false);
@Override
public String getMethodName() {
return "BeanCopier.create";
}
@Override
public ToBean callMethod(FromBean frombean) throws Exception {
ToBean toBean = new ToBean();
bc.copy(frombean, toBean, null);
return toBean;
}
};
// 数量较少的时候,测试性能 1 10 100 1000 10000
BenchmarkTest bt = new BenchmarkTest(10);
bt.benchmark(beanutilCB, fb);
bt.benchmark(propertyCB, fb);
bt.benchmark(springCB, fb);
bt.benchmark(cglibCB, fb);
}
}
3.结果
进行了三次测试,最后的结果如下:
1次测验 | 第一次(ms) | 第二次(ms) | 第三次(ms) | 平均值(ms) |
---|---|---|---|---|
BeanUtil.copyProperties | 107 | 103 | 100 | 103.33 |
PropertyUtils.copyProperties | 0 | 0 | 0 | 0 |
org.springframework.beans.BeanUtils.copyProperties | 61 | 65 | 59 | 61.3 |
BeanCopier.create | 0 | 0 | 0 | 0 |
100次测验 | 第一次(ms) | 第二次(ms) | 第三次(ms) | 平均值(ms) |
---|---|---|---|---|
BeanUtil.copyProperties | 134 | 113 | 127 | 124.66 |
PropertyUtils.copyProperties | 1 | 2 | 2 | 1.666 |
org.springframework.beans.BeanUtils.copyProperties | 64 | 61 | 66 | 63.66 |
BeanCopier.create | 0 | 0 | 0 | 0 |
10000次测验 | 第一次(ms) | 第二次(ms) | 第三次(ms) | 平均值(ms) |
---|---|---|---|---|
BeanUtil.copyProperties | 566 | 607 | 531 | 568 |
PropertyUtils.copyProperties | 20 | 21 | 19 | 20 |
org.springframework.beans.BeanUtils.copyProperties | 88 | 91 | 85 | 88 |
BeanCopier.create | 1 | 2 | 2 | 1.666 |
4.总结
不过需要注意的是,Cglib在测试的时候,先进行了实例的缓存,这个也是他性能较好的原因之一。如果把缓存去掉的话,性能就会出现了一些的差异,但是整体的性能还是很好,不过奇怪的是10000次反而比10次少,而且后面的反转1万次反而耗时最少,进行多次测试效果也是如此。 从整体的表现来看,Cglib的BeanCopier的性能是最好的无论是数量较大的1万次的测试,还是数量较少10次,几乎都是趋近与零损耗,Spring是在次数增多的情况下,性能较好,在数据较少的时候,性能比PropertyUtils的性能差一些。PropertyUtils的性能相对稳定,表现是呈现线性增长的趋势。而Apache的BeanUtil的性能最差,无论是单次Copy还是大数量的多次Copy性能都不是很好。
下一期和大家分享这四个性能差距的原因 敬请期待!
大家有任何问题欢迎评论区留言 或者加qq邮箱:1435469553@qq.com