java bean的转换工具目前主要有两大类,一类是通过反射实现的,如BeanUtils,一类是通过直接映射字节码实现的,如BeanCopier、Orika等。个人觉得这两类在性能上根本没有可比性。但是项目中总有人对BeanUtils这个工具类有种莫名的执着,因此本文将从bean转换的正确性、时间效率和易用性三个方面来比较BeanUtils、BeanCopier和Orika。
定义一个稍复杂点儿的实体类
private String field1;
private String field2;
private String field3;
private Strin field4;
private String field5;
private String field6;
private String field7;
private String field8;
private String field9;
private String field10;
private List<ListBean> field11;
private Map<String, MapBean> field12;
1. 比较正确性:
三个工具均能正确复制bean的各个属性;
2. 比较时间效率:
BeanUtils
long start = System.currentTimeMillis();
for(int i= 0; i<10000; i++) {
BeanUserCopy beanUserCopy = new BeanUserCopy();
BeanUtils.copyProperties(user, beanUserCopy);
}
long end = System.currentTimeMillis();
System.out.println("BeanUtils耗时:"+(end - start));
BeanCopier
BeanCopier copier = BeanCopier.create(BeanUser.class, BeanUserCopy.class, false);
long startCopier = System.currentTimeMillis();
for(int i= 0; i<10000; i++) {
BeanUserCopy beanUserCopy = new BeanUserCopy();
copier.copy(user, beanUserCopy, null);
}
long endCopier = System.currentTimeMillis();
System.out.println("BeanCopier耗时:"+(endCopier - startCopier));
Orika
MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
MapperFacade mapper = mapperFactory.getMapperFacade();
long startMapper = System.currentTimeMillis();
for(int i= 0; i<10000; i++) {
BeanUserCopy beanUserCopy = new BeanUserCopy();
beanUserCopy = mapper.map(user, BeanUserCopy.class);
}
long endMapper = System.currentTimeMillis();
System.out.println("MapperFacade耗时:"+(endMapper - startMapper));
执行10000次复制所用时间,单位ms:
BeanUtils:1623
BeanCopier:12
Orika:1460
很奇怪,Orika的性能完全不如预期???
在增加classMap之后耗时大幅度降低了
mapperFactory.classMap(BeanUser.class, BeanUserCopy.class)
.register();
最后10000次复制耗时331。
3. 比较易用性:
(1)缺少set方法的情况
当我故意将目标类中某个属性的set方法去掉之后,BeanUtils和BeanCopier还是能成功复制,但是缺少set方法的属性不会复制,而Orika直接报错了。
(2)类型相同名称不同的属性转换
三者结果一致,被修改属性名的那项都转换失败了。
orika提供如下的方法注册字段映射:
mapperFactory.classMap(BeanUser.class, BeanUserCopy.class)
.field("field2","change2")
.byDefault()
.register();
orika的映射还是很强大的,不仅可以映射这样的简单属性,还可以映射数组、List、类属性。
(3)类型不同名称相同的属性转换
将源bean中某一属性类型改为int,目标bean中改为Integer,BeanUtils和Orika转换成功了,而BeanCopier失败。
(4)自定义转换器
BeanCopier
修改源bean
private String field1;
private String field2;
private Integer field3;
private Date field4;
private String field5;
private String field6;
private String field7;
private String field8;
private String field9;
private String field10;
private List<ListBean> field11;
private Map<String, MapBean> field12;
目标bean
private String field1;
private String change2;
private int field3;
private String field4;
private String field5;
private String field6;
private String field7;
private String field8;
private String field9;
private String field10;
private List<ListBean> field11;
private Map<String, MapBean> field12;
定义转换器
class DateConverterBeanCopier implements Converter {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@Override
public Object convert(Object value, Class target, Object context) {
if(value instanceof Date){
Date date = (Date) value;
return sdf.format(date);
}else if(value instanceof Integer){
return (Integer)value;
}
return null;
}
}
BeanUserCopy beanUserCopyTest2 = new BeanUserCopy();
BeanCopier copierTest = BeanCopier.create(BeanUser.class, BeanUserCopy.class, true);
DateConverterBeanCopier converterBeanCopier = new DateConverterBeanCopier();
copierTest.copy(user, beanUserCopyTest2, converterBeanCopier);
除change2都转换成功了,也就是说同名不同类型的属性BeanCopier可以通过转换器实现。
Orika
定义转换器
//Date为源bean中类型,String为目标bean中类型
class DateConverterOrika extends CustomConverter<Date,String> {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@Override
public String convert(Date date, Type<? extends String> type, MappingContext mappingContext) {
return sdf.format(date);
}
}
MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
DateConverterOrika dateConverterOrika = new DateConverterOrika();
ConverterFactory converterFactory = mapperFactory.getConverterFactory();
converterFactory.registerConverter("myDateConverter", dateConverterOrika);
mapperFactory.classMap(BeanUser.class, BeanUserCopy.class)
.field("field2","change2")
.byDefault()
.fieldMap("field4")//field4为需要使用转换器的属性名
.converter("myDateConverter").add()
.register();
MapperFacade mapper = mapperFactory.getMapperFacade();
BeanUserCopy beanUserCopyTest3 = mapper.map(user, BeanUserCopy.class);
完美转换所有属性。
4. 总结
(1)orika映射功能很好用,在不适用转换器的情况下BeanCopier只会复制同名同类型的属性,即使int和Integer也会被BeanCopier认为是两种类型。
(2)orika和BeanCopier都支持自定义转换器,orika的转换器灵活度更高,但配置也会相对复杂一些。
(3)性能上还是BeanCopier胜出,在复制同名同类型属性时优先选择BeanCopier。