常见Bean映射工具分析评测及Orika介绍
Bean映射工具选择
工作中,我们经常需要将对象转换成不同的形式以适应不同的api,或者在不同业务层中传输对象而不同分层的对象存在不同的格式,因此我们需要编写映射代码将对象中的属性值从一种类型转换成另一种类型。
进行这种转换除了手动编写大量的get/set
代码,还可以使用一些方便的类库,常用的有apache的BeanUtils
,spring的BeanUtils
,cglib的BeanCopier
。
BeanUtils
apache的BeanUtils
和spring的BeanUtils
中拷贝方法的原理都是先用jdk中 java.beans.Introspector
类的getBeanInfo()
方法获取对象的属性信息及属性get/set方法,接着使用反射(Method
的invoke(Object obj, Object... args)
)方法进行赋值。apache支持名称相同但类型不同的属性的转换,spring支持忽略某些属性不进行映射,他们都设置了缓存保存已解析过的BeanInfo
信息。
BeanCopier
cglib的BeanCopier
采用了不同的方法:它不是利用反射对属性进行赋值,而是直接使用ASM的MethodVisitor
直接编写各属性的get/set
方法(具体过程可见BeanCopier
类的generateClass(ClassVisitor v)
方法)生成class文件,然后进行执行。由于是直接生成字节码执行,所以BeanCopier
的性能较采用反射的BeanUtils
有较大提高,这一点可在后面的测试中看出。
Dozer
使用以上类库虽然可以不用手动编写get/set
方法,但是他们都不能对不同名称的对象属性进行映射。在定制化的属性映射方面做得比较好的有Dozer,Dozer支持简单属性映射、复杂类型映射、双向映射、隐式映射以及递归映射。可使用xml或者注解进行映射的配置,支持自动类型转换,使用方便。但Dozer底层是使用reflect包下Field
类的set(Object obj, Object value)
方法进行属性赋值,执行速度上不是那么理想。
Orika
那么有没有特性丰富,速度又快的Bean映射工具呢,这就是下面要介绍的Orika,Orika是近期在github活跃的项目,底层采用了javassist类库生成Bean映射的字节码,之后直接加载执行生成的字节码文件,因此在速度上比使用反射进行赋值会快很多,下面详细介绍Orika的使用方法。
Orika使用
依赖
<dependency>
<groupId>ma.glasnost.orika</groupId>
<artifactId>orika-core</artifactId>
<version>1.5.2</version><!-- or latest version -->
</dependency>
简单映射
- 构造一个MapperFactory
MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
- 注册字段映射
mapperFactory.classMap(PersonSource.class, PersonDestination.class)
.field("firstName", "givenName")
.field("lastName", "sirName")
.byDefault()
.register();
- 进行映射
MapperFacade mapper = mapperFactory.getMapperFacade();
PersonSource source = new PersonSource();
// set some field values
...
// map the fields of 'source' onto a new instance of PersonDest
PersonDest destination = mapper.map(source, PersonDest.class);
在第二步进行的字段映射是双向的,我们可以从目标类型映射回源类型,byDefault()
方法用于注册名称相同的属性(如果所有属性名称都相同则可以省略第2步),如果不希望某个字段参与映射,可以使用exclude
方法
复杂映射
数组和List的映射
如果在目标类和目的类中分别有下面的属性
class BasicPerson {
private List<String> nameParts;
// getters/setters omitted
}
class BasicPersonDto {
private String firstName;
private String lastName;
// getters/setters omitted
}
可以使用下面的方式进行映射:
mapperFactory.classMap(BasicPerson.class, BasicPersonDto.class)
.field("nameParts[0]", "firstName")
.field("nameParts[1]", "lastName")
.register();
类类型的映射
class Name {
private String first;
private String last;
private String fullName;
// getters/setters
}
class BasicPerson {
private Name name;
// getters/setters omitted
}
class BasicPersonDto {
private String firstName;
// getters/setters omitted
}
使用:
mapperFactory.classMap(BasicPerson.class, BasicPersonDto.class)
.field("name.first", "firstName")
.register();
自定义转换器
orika同样支持自定义转换器,将指定类型或指定名称的属性做映射时添加自定义操作,例如,将String类型的或某个属性映射后加一个前缀,或者将Integer类型映射后加1等:
public class MyConverter extends CustomConverter<Date,MyDate> {
public MyDate convert(Date source, Type<? extends MyDate> destinationType) {
// return a new instance of destinationType with all properties filled
//example:source + 1;
}
}
Date
为源类型中要做转换的属性数据类型,例如String
、Integer
等,MyDate
为目标类型中要做转换的属性数据类型。
如果需要定义全局范围的转换:
ConverterFactory converterFactory = mapperFactory.getConverterFactory();
converterFactory.registerConverter(new MyConverter());
如果仅需要某几个属性使用转换器:
ConverterFactory converterFactory = mapperFactory.getConverterFactory();
converterFactory.registerConverter("myConverterIdValue", new MyConverter());
mapperFactory.classMap( Source.class, Destination.class )
.fieldMap("sourceField1", "sourceField2").converter("myConverterIdValue").add()
...
.register();
其他说明
Orika支持递归映射,将映射嵌套类直到用“简单”类型完成映射。它还包含故障保险,以正确处理正在尝试映射的对象中的递归引用。
在于spring集成时,可以将MapperFactory设置为单例
各映射工具的性能测试
构造一个包含普通类型及类类型的Bean对象,使用jmh微基准框架进行测试。由于jvm会对热点代码进行优化:方法反射调用次数超过阈值时会生成一个专用的MethodAccessor实现类,生成其中的invoke()方法的字节码进行执行。
故测试时每种方法先预热执行15次,而后再执行100次获取每次执行的平均时间:
Benchmark Mode Samples Score Score error Units
o.s.MyBenchmark.apache avgt 100 25.246 0.535 us/op
o.s.MyBenchmark.beanCopier avgt 100 0.004 0.000 us/op
o.s.MyBenchmark.byHand avgt 100 0.004 0.000 us/op
o.s.MyBenchmark.dozer avgt 100 5.855 0.260 us/op
o.s.MyBenchmark.orika avgt 100 0.353 0.017 us/op
o.s.MyBenchmark.spring avgt 100 0.627 0.020 us/op
统计报告中Units单位为微秒/次,由Score项可以看出,基于ASM的cglib BeanCopier拷贝速度基本和手写get/set方法的速度无异,其次的就是基于javassist的Orika了,Orika的速度是spring BeanUtils的两倍,Dozer的20倍,Apache BeanUtils的120倍。
综上,当属性名和属性类型完全相同时使用BeanCopier是最好的选择,当存在属性名称不同或者属性名称相同但属性类型不同的情况时,使用Orika是一种不错的选择。如果你对Orika感到不放心,实际应用前可以写个测试类查看它的转换结果是否符合预期。
</div>
常见Bean映射工具分析评测及Orika介绍的更多相关文章
- 5种常见Bean映射工具的性能比对
本文由 JavaGuide 翻译自 https://www.baeldung.com/java-performance-mapping-frameworks .转载请注明原文地址以及翻译作者. 1. ...
- Bean映射工具之Apache BeanUtils VS Spring BeanUtils
背景 在我们实际项目开发过程中,我们经常需要将不同的两个对象实例进行属性复制,从而基于源对象的属性信息进行后续操作,而不改变源对象的属性信息,比如DTO数据传输对象和数据对象DO,我们需要将DO对象进 ...
- Java bean常见映射工具分析和比较
1. 概述 日常Java开发项目中,我们经常需要将对象转换成其他形式的对象,因此我们需要编写映射代码将对象中的属性值从一种类型转换成另一种类型. 进行这种转换除了手动编写大量的get/set代码,还可 ...
- cglib、orika、spring等bean copy工具性能测试和原理分析
简介 在实际项目中,考虑到不同的数据使用者,我们经常要处理 VO.DTO.Entity.DO 等对象的转换,如果手动编写 setter/getter 方法一个个赋值,将非常繁琐且难维护.通常情况下,这 ...
- 几种常见的JavaScript混淆和反混淆工具分析实战
几种常见的JavaScript混淆和反混淆工具分析实战 xiaix2016-03-05+8共1195751人围观 ,发现 5 个不明物体WEB安全 信息安全常被描述成一场军备竞赛,白帽与黑帽,渗透测试 ...
- java Bean的映射工具
数据层,业务逻辑层和表现层,每一层对应的应该是不一样的对象,所以就有个这么个java bean的映射工具 dozer.......................................... ...
- 使用MAT(Memory Analyzer Tool)工具分析dump文件--转
原文地址:http://gao-xianglong.iteye.com/blog/2173140?utm_source=tuicool&utm_medium=referral 前言 生产环境中 ...
- Touch 方法&属性 映射工具
Touch 方法&属性 映射工具(0.5 版本) 标签 : github 线上后门与接口调试: 原先需要测试一个接口(如Dubbo.DAO), 或为线上留后门, 需要写大量的Web层(Api. ...
- IntentActionUtil【Intent的常见作用的工具类】
版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 主要用于通过Intent调用手机本地软件打开文件(doc.xsl.pdf.ppt.mp3.mp4等格式).安装apk.发送邮件.拨打 ...
随机推荐
- c++ 如何对拍
首先要写好两个要对拍程序(假设是A,B),和一个制造数据的程序(设为made) (要放在同一文件夹内) 编译得到A.exe , B.exe , made.exe 写一个对拍器 格式如下 @ech ...
- JDK使用最多的模式之一--观察者模式
公司接到新任务,需要做一个气象监测应用.该应用将实现三个界面:当前气象状态,气象统计以及气象预报.应用从WeatherObject对象中获取所需数据:温度,湿度,气压.当然,为了可扩展性,该应用同时也 ...
- SQL数据库学习,常用语句查询大全
数据库学习 sql server数据库基本概念 使用文件保存数据存在几个缺点: 1.文件的安全性问题: 2.文件不利于查询和对数据的管理: 3.文件不利于存放海量数据 4.文件在程序中控制不方便. 数 ...
- IOStableviewsectionSet
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { if (t ...
- 【HEVC帧间预测论文】P1.8 Complexity Control of High Efficiency Video Encoders for Power-Constrained Devices
参考:Complexity Control of High Efficiency Video Encoders for Power-Constrained Devices <HEVC标准介绍.H ...
- vscode 用户代码片段 vue初始化模板 Snippet #新加入开头注释 自动生成文件名 开发日期时间等内容
vue文件模板 模板变量 https://code.visualstudio.com/docs/editor/userdefinedsnippets#_variables vue.json { // ...
- render_to_response()
render_to_response('模板名称',字典) 字典:第二个参数必须是为该模板创建context时所使用的字典,如果不提供第二个参数,render_response()使用一个空字典
- 【转】关于编写WPF UserControl时提示The name 'InitializeComponent' does not exist in the current contextr的解决!
1.打开.csproj(工程)文件. 2.找到<Import Project="$(MSBuildBinPath)/Microsoft.CSharp.targets" /&g ...
- Jenkins出现 {"changed": false, "msg": "mkdir: cannot create directory ‘/root/.ansible/tmp/ansible-tmp-1552390265.8-253945919613076’: No space left on device\n", "unreachable": true}
之前部署Jenkins没有遇到过这个问题,百度也找不到相关内容,无奈用过google翻译了一下,显示内存不足,上到服务器查询df -h 发现use% 100% 由于日志等文件导致服务器磁盘空间满了,引 ...
- cmd启动MySQL服务器发生错误
Mysql net start mysql启动,提示发生系统错误 5 拒绝访问 原文:https://blog.csdn.net/angel_guoo/article/details/7919037 ...