欢迎您访问365答案网,请分享给你的朋友!
生活常识 学习资料

几种Bean映射工具介绍

时间:2023-08-04
文章目录

一、介绍

1、功能介绍2、不同方法与性能对比 二、转换案例

1、源VO和目标VO2、get/set3、Json2Json4、Spring copyProperties✨5、BeanCopier6、MapStruct✨✨ 一、介绍 1、功能介绍

在 Java 系统工程开发过程中,都会有各个层之间的对象转换,比如 VO、DTO、PO、VO 等,而如果都是手动get、set又太浪费时间,还可能操作错误,所以选择一个自动化工具会更加方便

2、不同方法与性能对比

目前用于对象属性转换有12种,包括普通的get/set、json2Json、Apache属性拷贝、Spring属性拷贝、bean-mapping、bean-mapping-asm、BeanCopier、Orika、Dozer、ModelMapper、JMapper、MapStruct

Spring 提供的BeanUtils.copyProperties 是大家代码里最常出现的工具类,注意不是 Apache 包下手动get、set性能是最好的,另外 MapStruct 性能和操作也很方便,因为它本身就是在编译期生成get、set代码,和我们写get、set一样其他一些组件包主要基于 AOP、ASM、CGlib,的技术手段实现的,所以也会有相应的性能损耗
二、转换案例 1、源VO和目标VO

//源类@Data@AllArgsConstructor@NoArgsConstructor@Accessors(chain = true)public class SourceVO { private String name; private String password; private Integer age;}//目标类@Data@Accessors(chain = true)public class TargetVO { private String name; private String password; private Integer age;}

2、get/set

这种方式也是日常使用的最多的,性能十分优秀,但是操作起来有点麻烦。多个get/set可以通过 Shift+Alt 选中所有属性,Shift+Tab 归并到一列,接下来在使用 Alt 选中这一列,批量操作粘贴 targetVO.set 以及快捷键大写属性首字母,最后切换到结尾补充括号和分号,最终格式化一下就可以了

3、Json2Json

把对象转JSON串,再把JSON转另外一个对象,这种方式性能不是很高

public class ObjectConversion { public static List copy(List<?> list,Class clazz){ String oldOb = JSON.toJSONString(list); return JSON.parseArray(oldOb, clazz); } public static T copy(Object ob, Class clazz){ String oldOb = JSON.toJSONString(ob); return JSON.parseObject(oldOb, clazz); }}

public class Test { public static void main(String[] args){ SourceVO sourceVO = new SourceVO("shawn","123456",18); TargetVO targetVO = ObjectConversion.copy(sourceVO, TargetVO.class); System.out.println(targetVO); }}

4、Spring copyProperties✨

这个方法是反射的属性拷贝,Spring 提供的 copyProperties 要比 Apache 好用的多,性能和操作都比较好,这个包是org.springframework.beans.BeanUtils

另外这里考虑到属性不同的字段的拷贝,还额外创建了一个回调函数进行映射处理

//回调函数定义,这里用了java8特性函数式接口@FunctionalInterfacepublic interface BeanCopyUtilCallBack { void callBack(S t, T s);}

public class BeanCopyUtil extends BeanUtils { public static List copyListProperties(List sources, Supplier target) { return copyListProperties(sources, target, null); } public static List copyListProperties(List sources, Supplier target, BeanCopyUtilCallBack callBack) { List list = new ArrayList<>(sources.size()); for (S source : sources) { T t = target.get(); copyProperties(source, t); list.add(t); if (callBack != null) { // 回调 callBack.callBack(source, t); } } return list; }}

这里的回调函数可以使用Enum枚举进行规范化,最终进行测试,成功拷贝

ublic class Test { public static void main(String[] args){ // 单个拷贝 SourceVO sourceBean = new SourceVO("shawn", "123456", 18); TargetVO targetBean = new TargetVO(); BeanUtils.copyProperties(sourceBean,targetBean); System.out.println(targetBean); // 列表拷贝 List sourceVOList = new ArrayList<>(); sourceVOList.add(new SourceVO("shawn","123456",18)); sourceVOList.add(new SourceVO("shawn1","12345678",20)); List targetVOList= BeanCopyUtil.copyListProperties(sourceVOList, TargetVO::new, (sourceVO,targetVO)->{ targetVO.setAge(sourceVO.getAge()); }); System.out.println(targetVOList); }}

5、BeanCopier

Cglib BeanCopier 的原理与Beanutils 原理不太一样,其主要使用 字节码技术动态生成一个代理类,代理类实现get 和 set方法。生成代理类过程存在一定开销,但是一旦生成,我们可以缓存起来重复使用,所有 Cglib 性能相比Beanutils 性能比较好整体性能不错,使用也不复杂

//工具类public class WrapperBeanCopier { public static T copyProperties(M source, Class clazz){ if (Objects.isNull(source) || Objects.isNull(clazz)) throw new IllegalArgumentException(); return copyProperties(source, clazz, null); } public static List copyObjects(List sources, Class clazz) { if (Objects.isNull(sources) || Objects.isNull(clazz) || sources.isEmpty()) throw new IllegalArgumentException(); BeanCopier copier = BeanCopier.create(sources.get(0).getClass(), clazz, false); return Optional.of(sources) .orElse(new ArrayList<>()) .stream().map(m -> copyProperties(m, clazz, copier)) .collect(Collectors.toList()); } private static T copyProperties(M source, Class clazz, BeanCopier copier){ if (null == copier){ copier = BeanCopier.create(source.getClass(), clazz, false); } T t = null; try { t = clazz.newInstance(); copier.copy(source, t, null); } catch (InstantiationException | IllegalAccessException e) { e.printStackTrace(); } return t; }}

public class Test { public static void main(String[] args){ // 单个拷贝 SourceVO sourceVO = new SourceVO("shawn", "123456", 18); TargetVO targetVO = WrapperBeanCopier.copyProperties(sourceVO, TargetVO.class); System.out.println(targetVO); // 列表拷贝 List sourceVOList = new ArrayList<>(); sourceVOList.add(new SourceVO("shawn","123456",18)); sourceVOList.add(new SourceVO("shawn1","12345678",20)); List targetVOS = WrapperBeanCopier.copyObjects(sourceVOList, TargetVO.class); System.out.println(targetVOS); }}

如果要自定义转换,可以使用new Converter(),一旦我们自己打开使用转换器,所有属性复制都需要我们自己来了,否则会导致无法复制。另外这里需要注意的是拷贝对象要去除@Accessors(chain = true)注解,因为该注解会将 setter 方法的返回值由 void 修改为当前对象。这导致 setter 的方法签名改变,最终导致 BeanCopier 无法识别现有的 setter 方法

6、MapStruct✨✨

官网文档地址:https://github.com/mapstruct/mapstruct

MapStruct是一款基于Java注解的对象属性映射工具,在Github上已经有4.5K+Star。使用的时候我们只要在接口中定义好对象属性映射规则,它就能自动生成映射实现类,不使用反射,性能优秀,能实现各种复杂映射。(强烈推荐)

首先导入Maven依赖

org.mapstruct mapstruct 1.4.2.Final org.mapstruct mapstruct-processor 1.4.2.Final compile

创建Bean对象

@Data@AllArgsConstructor@NoArgsConstructor@Accessors(chain = true)public class SourceVO { private String name; private String password; private Integer age; private Date birthday;}@Data@AllArgsConstructor@NoArgsConstructor@Accessors(chain = true)public class TargetVO { private String name; private String pwd; private Integer age; private String birthday;}

编写Mapper文件,实现同名同类型属性、不同名称属性、不同类型属性的映射

@Mapperpublic interface UserMapper { UserMapper INSTANCE = Mappers.getMapper(UserMapper.class); @Mapping(target = "pwd", source = "password") @Mapping(source = "birthday",target = "birthday", dateFormat = "yyyy-MM-dd") TargetVO getTargetVO(SourceVO sourceVO);}

最后直接通过接口中的INSTANCE实例调用转换方法

public class Test { public static void main(String[] args){ SourceVO sourceVO = new SourceVO("shawn", "123456", 18, new Date()); TargetVO targetVO = UserMapper.INSTANCE.getTargetVO(sourceVO); System.out.println(targetVO); }}

其实MapStruct的实现原理很简单,就是根据我们在Mapper接口中使用的@Mapper和@Mapping等注解,在运行时生成接口的实现类,我们可以打开项目的target目录查看

初次之外,MapStruct使用有:

基本映射、集合映射、子对象映射、合并映射使用依赖注入、使用常量/默认值/表达式、映射前后自定义、处理映射异常等

详情可参考:
https://blog.csdn.net/zhenghongcs/article/details/121349361
https://mapstruct.org/documentation/stable/reference/html/


参考文章

https://mp.weixin.qq.com/s/_QJa5RSxvPBsqXo8yS5-pg

Copyright © 2016-2020 www.365daan.com All Rights Reserved. 365答案网 版权所有 备案号:

部分内容来自互联网,版权归原作者所有,如有冒犯请联系我们,我们将在三个工作时内妥善处理。