我们在Java代码编写中经常会遇到DO 、DTO之间的对象隐射转换,我们在设计DO、DTO的时候一般会尽量让对象名称、对象属性保持一致,利于属性拷贝,但是现实场景中可能存在一些对象名称不一致、对象类型不一致的情况,不同的拷贝方案,性能与使用场景也可能存在不一样,那么在众多的对象拷贝框架中如何选择合适的使用呢?
常用对象属性拷贝方法:
- commons-beanutils 框架中的 BeanUtils
- commons-beanutils 框架中的 PropertyUtils
- ModelMapper
- Cglib中提供的BeanCopier, BulkBean,BeanMap,FastClass/FastMethod
- Orika
- Dozer
- HardCopy,手工硬编码
有人做了一张性能对比图:
HardCopy:
手工硬编码的方式是基本是效率最高的方式,但是当一个类有几十个属性的时候,代码编写效率低下,而且丑陋,最重要的是,当新扩展一个字段后,往往容易忽略在mapping convert文件中添加相应的属性隐射,给业务带来一定的潜在风险(error-prone)。硬编码并不是一无是处,当对象属性比较固定,且对性能要求非常高的时候,硬编码是最好的选择方案。但是很多Object的 大量属性需要编写Convert方法时,就感觉很浪费时间,有没有更有效率的编写方法呢?当然,我自己写了一个Convert方法生成器(密码: ycag,Console输出,需要复制粘贴),这样就可以大量节省硬编码ModelConvert的时间。
BeanUtils:
因为我们项目中最常用的就是Spring-bean,所以最先想到使用属性拷贝的就是Apache的BeanUtils.copyProperties方法。
工作原理:
BeanUtils通过反射机制的方式从orig中获取属性信息和属性的可读、可写方法(通过get、set方法,如果属性没有get、set方法,则此属性不会被拷贝),调用orig的get方法获取属性值,将属性值写入dest的对应属性中(使用dest的set方法)。可以看出BeanUtils是严格要求orig、dest必须符合bean规范,有get、set方法,无法完成不同属性名之间的隐射拷贝。
对象类型转换:
BeanUtils为了方便对象类型转换,还提供了ConvertUtilsBean类,此类可以注入一系列Converter对象,提供对象的初始值或者其他转换方法,比如设置Integer的默认值为0 或 从Date 类型到long类型,我们在使用过程中一般NULL与0是不一样的,所以不期望对象转换过程中默认设置值,所以会自定义对象类型转换器。由此说明BeanUtils是对属性类型没有强一致性判断的。
对象拷贝深度:待测试。
由于大量采用反射机制&严格的参数校验,所以性能较差,不建议使用。
PropertyUtils:
PropertyUtils实际上使用的也是beanUtils,但是在初始化的时候就会发现,BeanUtils初始化了ConvertUtilsBean和PropertyUtilsBean,而PropertyUtils只初始化了PropertyUtilsBean,说明PropertyUtils不支持属性类型自动转换的功能(如果类型不同则会抛出异常),而BeanUtils支持属性类型自动转换的功能,这也是两者的区别。
性能上,PropertyUtils 与 BeanUtils稍微好一点。
ModelMapper:
ModelMapper能用更加紧凑的代码对Java对象进行映射,在更简单的情况下甚至可以实现零配置。它支持以下特性:
- 基于名称的对象属性映射
- 复制公开的、受保护的和私有的字段
- 略过某些字段
- 可用转换器来影响映射(如将字符串转换为小写)
- 在不同类型的字段间进行映射(如将字符串转换为数字)
- 采用不同的条件进行映射
- 默认条件不充分时采用松散的映射策略
- 对映射过程进行验证以确保所有字段都被处理
- 对特殊情况下的映射过程进行完全可定制化的控制
- 与Guice或Spring集成
Cglib:BeanCopier
参考:http://agapple.iteye.com/blog/799827,据说性能比BeanUtils优一个数量级以上,但是其提供的功能也比较简单。
Orika:
Github中的源码:https://github.com/orika-mapper/orika
基于生成字节码的方式进行属性映射,是目前出了硬编码外性能最好的一款Object mapping工具,他比ModelMapper占用的内存会稍微多一点,空间换时间,这也符合计算机规律。不足时文档较少,API看起来有一些混乱,支持的特性不较少,其次一些属性的注册使用的是“弱类型”,当属性名称变更时容易被忽略(error-prone)。
Dozer:
虽然Dozer提供了丰富的特性支持,但是由于其性能较差,所以这里就不做介绍。
除了以上介绍的几种之外还有一些其他的Object mapping工具类,比如 mapping4java、GeDA等。
总结:
根据自己的需求选择一款合适且性能优雅的框架,如果觉得没有合适的,当然在时间和精力允许的情况下自己也可以去造一个轮子。
参考:
https://prezi.com/q7pd1ad2spro/copy-of-object-mapping-in-java/
http://stackoverflow.com/questions/1432764/any-tool-for-java-object-to-object-mapping