Java对象转换方案分析与mapstruct实践

转 阿里技术 一 前言 随着系统模块分层不断细化,在Java日常开发中不可避免地涉及到各种对象的转换,如:DO、DTO、VO等等,编写映射转换代码是一个繁琐重复且还易错的工作,一个好的工具辅助,减轻了工作量、提升开发工作效率的同时还能减少bug的发生。 二 常用方案及分析 1 fastjson 这种方案因为通过生成中间json格式字符串,然后再转化成目标对象,性能非常差,同时因为中间会生成json格式字符串,如果转化过多,gc会非常频繁,同时针对复杂场景支持能力不足,基本很少用。 2 BeanUtil类 BeanUtil.copyProperties()结合手写get、set,对于简单的转换直接使用BeanUtil,复杂的转换自己手工写get、set。该方案的痛点就在于代码编写效率低、冗余繁杂还略显丑陋,并且BeanUtil因为使用了反射invoke去赋值性能不高。 只能适合bean数量较少、内容不多、转换不频繁的场景。 apache.BeanUtils 这种方案因为用到反射的原因,同时本身设计问题,性能比较差。集团开发规约明确规定禁止使用。 spring.BeanUtils 这种方案针对apache的BeanUtils做了很多优化,整体性能提升不少,不过还是使用反射实现比不上原生代码处理,其次针对复杂场景支持能力不足。 3 beanCopier 这种方案动态生成一个要代理类的子类,其实就是通过字节码方式转换成性能最好的get和set方式,重要的开销在创建BeanCopier,整体性能接近原生代码处理,比BeanUtils要好很多,尤其在数据量很大时,但是针对复杂场景支持能力不足。 4 各种Mapping框架 分类 Object Mapping 技术从大的角度来说分为两类,一类是运行期转换,另一类则是编译期转换: 运行期反射调用 set/get 或者是直接对成员变量赋值。这种方式通过invoke执行赋值,实现时一般会采用beanutil, Javassist等开源库。运行期对象转换的代表主要是Dozer和ModelMaper。 编译期动态生成 set/get 代码的class文件,在运行时直接调用该class的 set/get 方法。该方式实际上仍会存在 set/get 代码,只是不需要开发人员自己写了。这类的代表是:MapStruct,Selma,Orika。 分析 无论哪种Mapping框架,基本都是采用xml配置文件 or 注解的方式供用户配置,然后生成映射关系。 编译期生成class文件方式需要DTO仍然有set/get方法,只是调用被屏蔽;而运行期反射方式在某些直接填充 field的方案中,set/get代码也可以省略。 编译期生成class方式会有源代码在本地,方便排查问题。 编译期生成class方式因为在编译期才出现java和class文件,所以热部署会受到一定影响。 反射型由于很多内容是黑盒,在排查问题时,不如编译期生成class方式方便。参考GitHub上工程java-object-mapper-benchmark可以看出主要框架性能比较。 反射型调用由于是在运行期根据映射关系反射执行,其执行速度会明显下降N个量级。 通过编译期生成class代码的方式,本质跟直接写代码区别不大,但由于代码都是靠模板生成,所以代码质量没有手工写那么高,这也会造成一定的性能损失。 综合性能、成熟度、易用性、扩展性,mapstruct是比较优秀的一个框架。 三 Mapstruct使用指南 1 Maven引入 2 […]

lWoHvYe 无悔,专一