文章转载自:Bean复制的几种框架性能比较
Bean复制的几种框架性能比较:
测试结果:
10次测验 | 第一次 | 第二次 | 第三次 | 平均值 | 每次平均值 |
---|---|---|---|---|---|
BeanUtil.copyProperties | 54 | 57 | 50 | 53.66667 | 5.366666667 |
PropertyUtils.copyProperties | 4 | 4 | 4 | 4 | 0.4 |
org.springframework.beans.BeanUtils.copyProperties | 12 | 10 | 11 | 11 | 1.1 |
BeanCopier.create | 0 | 0 | 0 | 0 | 0 |
10000次测验 | 第一次 | 第二次 | 第三次 | 平均值 | 每次平均值 |
---|---|---|---|---|---|
BeanUtil.copyProperties | 241 | 222 | 226 | 229.6667 | 0.022966667 |
PropertyUtils.copyProperties | 92 | 90 | 92 | 91.33333 | 0.009133333 |
org.springframework.beans.BeanUtils.copyProperties | 29 | 30 | 32 | 30.33333 | 0.003033333 |
BeanCopier.create | 1 | 1 | 1 | 1 | 0.1 |
10000次反转测验 | 第一次 | 第二次 | 第三次 | 平均值 | 每次平均值 |
---|---|---|---|---|---|
BeanUtil.copyProperties | 178 | 174 | 178 | 176.6667 | 0.017666667 |
PropertyUtils.copyProperties | 91 | 87 | 89 | 89 | 0.0089 |
org.springframework.beans.BeanUtils.copyProperties | 21 | 21 | 21 | 21 | 0.0021 |
BeanCopier.create | 0 | 1 | 1 | 0.666667 | 6.66667E-05 |
注意:Cglib在测试的时候,先进行了实例的缓存,这个也是他性能较好的原因之一。如果把缓存去掉的话,性能就会出现了一些的差异,但是整体的性能还是很好,不过奇怪的是10000次反而比10次少,而且后面的反转1万次反而耗时最少,进行多次测试效果也是如此。
从整体的表现来看:
- Cglib的BeanCopier的性能是最好的无论是数量较大的1万次的测试,还是数量较少10次,几乎都是趋近与零损耗。
- Spring是在次数增多的情况下,性能较好,在数据较少的时候,性能比PropertyUtils的性能差一些。
- PropertyUtils的性能相对稳定,表现是呈现线性增长的趋势。
- 而Apache的BeanUtil的性能最差,无论是单次Copy还是大数量的多次Copy性能都不是很好
测验次数 | 10次 | 10000次 | 10000次反转 |
---|---|---|---|
BeanCopier.create | 41 | 28 | 10 |
cglib BeanCopier 使用小探究:
- cglib是一款比较底层的操作java字节码的框架,其特性包括:
- 属性名称、类型都相同:拷贝OK
- 属性名称相同、类型不同:名称相同而类型不同的属性不会被拷贝
注意:即使源类型是原始类型(int, short和char等),目标类型是其包装类型(Integer, Short和Character等),或反之:都不会被拷贝。 - 源类和目标类有相同的属性(两者的getter都存在),但目标类的setter不存在 :
创建BeanCopier的时候抛异常。
网上很多文章说是这里抛得异常:
我信你个鬼,糟老头子
Debug追踪看一下代码执行过程,根本就没有执行generateClass方法中的代码呢(如果写错了,欢迎大家来吐槽,一起分析),也没有抛空指针异常,难道版本不一样吗!
- 源类或目标类的setter比getter少 : 拷贝OK。此时的setter多余,但不会报错。
当源和目标类的属性类型不同时,不能拷贝该属性,此时我们可以通过实现Converter接口来自定义转换器:
直接上代码吧:
package com.pica.cloud.patient.resident.server;
/**
* @ClassName TargetClazz
* @Description 目标类
* @Author Chongwen.jiang
* @Date 2019/7/25 17:18
* @ModifyDate 2019/7/25 17:18
* @Version 1.0
*/
public class TargetClazz {
private Integer id;
private String name;
private String createTime;
public Integer getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCreateTime() {
return createTime;
}
public void setCreateTime(String createTime) {
this.createTime = createTime;
}
public void setId(Integer id) {
this.id = id;
}
}
package com.pica.cloud.patient.resident.server;
import java.sql.Timestamp;
/**
* @ClassName SourceClazz
* @Description 源类
* @Author Chongwen.jiang
* @Date 2019/7/25 17:17
* @ModifyDate 2019/7/25 17:17
* @Version 1.0
*/
public class SourceClazz {
private Integer id;
private String name;
private Timestamp createTime;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Timestamp getCreateTime() {
return createTime;
}
public void setCreateTime(Timestamp createTime) {
this.createTime = createTime;
}
}
package com.pica.cloud.patient.resident.server;
import org.springframework.cglib.core.Converter;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
/**
* @ClassName AccountConverter
* @Description Bean属性类型转换器
* @Author Chongwen.jiang
* @Date 2019/7/25 17:56
* @ModifyDate 2019/7/25 17:56
* @Version 1.0
*/
public class TypeConverter implements Converter {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@Override
public Object convert(Object value, Class target, Object o1) {
if (value instanceof Integer) {
return (Integer) value;
} else if (value instanceof Timestamp) {
Timestamp date = (Timestamp) value;
return sdf.format(date);
} else if (value instanceof BigDecimal) {
BigDecimal bd = (BigDecimal) value;
return bd.toPlainString();
}
return null;
}
}
package com.pica.cloud.patient.resident.server;
import com.alibaba.fastjson.JSON;
import com.pica.cloud.patient.resident.server.mapper.WechatUserMapper;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.test.context.junit4.SpringRunner;
import java.sql.Timestamp;
/**
* @ClassName DbTest
* @Description 测试类
* @Author Jun.li0101
* @Date 2019/3/21 13:25
* @ModifyDate 2019/3/21 13:25
* @Version 1.0
*/
@SpringBootTest
@RunWith(SpringRunner.class)
public class DbTest {
@Autowired
private WechatUserMapper wechatUserMapper;
@Test
public void test1() {
SourceClazz sourceClazz = new SourceClazz();
sourceClazz.setId(12);
sourceClazz.setName("loname");
sourceClazz.setCreateTime(Timestamp.valueOf("2019-07-25 15:54:23"));
final BeanCopier a = BeanCopier.create(SourceClazz.class, TargetClazz.class, true);
TargetClazz targetClazz = new TargetClazz();
TypeConverter typeConvert = new TypeConverter();
a.copy(sourceClazz, targetClazz, typeConvert);
System.out.println(JSON.toJSONString(targetClazz));
Assert.assertEquals("2019-07-25 15:54:23", targetClazz.getCreateTime());
}
}
注:一旦使用Converter,BeanCopier只使用Converter定义的规则去拷贝属性,所以在convert方法中要考虑所有的属性。