使用MapStruct 解决对象之间转换、深拷贝问题

2024-05-26 19:18

本文主要是介绍使用MapStruct 解决对象之间转换、深拷贝问题,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在日常开发中,我们会定义多种不同的Javabean,比如DTO(Data Transfer Object:数据传输对象),DO(Data Object:数据库映射对象,与数据库一一映射),VO(View Object:显示层对象,通常是 Web 向模板渲染引擎层传输的对象)等等这些对象。在这些对象与对象之间转换通常是调对象的set和get方法进行复制,这种转换通常也是很无聊的操作,如果有一个专门的工具来解决Javabean之间的转换问题,让我们从这种无聊的转换操作中解放出来。

首先梳理出来现在有哪些对象拷贝的方式:

  • 浅拷贝
    • Apache的BeanUtils基于反射实现,BeanUtils是Apache commens组件里面的成员,由Apache提供的一套开源api,用于简化对javaBean的操作,能够对基本类型自动转换。
    • Spring的BeanUtils基于反射实现,BeanUtils是Spring框架提供的对Java反射和自省API的包装。其主要目的是利用反射机制对JavaBean的属性进行处理。
    • BeanCopier动态代理实现,BeanCopier是Cglib包中的一个类,用于对象的复制。目标对象必须先实例化 而且对象必须要有setter方法。由于BeanCopier是动态代理实现所以性能上比前两个要好的多
  • 深拷贝
    • Mapstruct:MapStruct是一个Java注释处理器,用于生成类型安全的bean映射类。MapStruct具有以下优点:
      • 速度快:使用普通的方法代替反射
      • 编译时类型安全性 : 只能映射彼此的对象和属性,不会将商品实体意外映射到用户DTO等
数据量ApacheSpringMapStructBeanCopier
100w391ms250ms45ms57ms
10w82ms34ms8ms10ms
1w30ms19ms2ms7ms
1k15ms6ms1ms5ms
1005ms3ms1ms4ms
102ms1ms1ms4ms

根据测试结果,我们可以得出在速度方面,MapStruct是最好的,执行速度是 Apache BeanUtils 的10倍、Spring BeanUtils 的 4-5倍、和 BeanCopier 的速度差不多。
由此可以看出,在大数据量级的情况下,MapStruct 和 BeanCopier 都有着较高的性能优势,其中 MapStruct 尤为优秀。如果你仅是在日常处理少量的对象时,选取哪个其实变得并不重要,但数据量大时建议还是使用MapStruct 或 BeanCopier 的方式,提高接口性能。

 

实战示例

maven依赖引入

方式1

<dependency><groupId>org.mapstruct</groupId><artifactId>mapstruct-jdk8</artifactId><version>1.3.0.Final</version>
</dependency>
<dependency><groupId>org.mapstruct</groupId><artifactId>mapstruct-processor</artifactId><version>1.3.0.Final</version><scope>provided</scope>
</dependency>

方式2

 <dependency><groupId>org.mapstruct</groupId><artifactId>mapstruct</artifactId><version>1.4.2.Final</version>
</dependency><build><!-- 配置lombok 和mapStruct注解处理器 --><pluginManagement><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><source>1.8</source><target>1.8</target><annotationProcessorPaths><path><groupId>org.mapstruct</groupId><artifactId>mapstruct-processor</artifactId><version>1.4.2.Final</version></path><path><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.12</version></path></annotationProcessorPaths></configuration></plugin></plugins></pluginManagement>
</build>

实体对象

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Account {// 账号private String account;// 金额private Integer amt;
}
@Data
@AllArgsConstructor
public class User {private String userName;private List<String> addresses;
}
@Data
public class CreateAccountDTO {private String key;private List<Account> accounts;
}
@Data
public class CreateAccountVO {private String visitKey;private User user;private List<Account> accounts;
}

转换

import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;@Mapper
public interface AccountConvertUtils {AccountConvertUtils INSTANCE = Mappers.getMapper(AccountConvertUtils.class);// 普通的映射CreateAccountVO convert(CreateAccountDTO createAccountDTO);// 类型转换的映射@Mappings({@Mapping(target = "visitKey", source = "key"),})CreateAccountVO convertWithMapping(CreateAccountDTO createAccountDTO);// 多对一映射@Mappings({@Mapping(target = "visitKey", source = "createAccountDTO.key"),})CreateAccountVO convert(User user, CreateAccountDTO createAccountDTO);
}

测试用例

public class DemoApp {public static void main(String[] args) {CreateAccountDTO createAccountDTO = new CreateAccountDTO();List<Account> accounts = new ArrayList<>();accounts.add(new Account("ACT2021051200000001", 1000));accounts.add(new Account("ACT2021051200000002", 5000));createAccountDTO.setKey("326D1478E3914C8A");createAccountDTO.setAccounts(accounts);User user = new User("Jaemon", Lists.newArrayList("SZ", "NS"));// 执行1
//        CreateAccountVO createAccountVO = AccountConvertUtils.INSTANCE.convert(createAccountDTO);// 执行2
//        CreateAccountVO createAccountVO = AccountConvertUtils.INSTANCE.convertWithMapping(createAccountDTO);// 执行3CreateAccountVO createAccountVO = AccountConvertUtils.INSTANCE.convert(user, createAccountDTO);createAccountDTO.getAccounts().add(new Account("ACT2021051200000003", 1010));System.out.println(createAccountDTO.toString());System.out.println(createAccountVO.toString());}
}

运行输出

// 执行1打印
CreateAccountDTO(key=326D1478E3914C8A, accounts=[Account(account=ACT2021051200000001, amt=1000), Account(account=ACT2021051200000002, amt=5000), Account(account=ACT2021051200000003, amt=1010)])
CreateAccountVO(visitKey=null, user=null, accounts=[Account(account=ACT2021051200000001, amt=1000), Account(account=ACT2021051200000002, amt=5000)])// 执行2打印    
CreateAccountDTO(key=326D1478E3914C8A, accounts=[Account(account=ACT2021051200000001, amt=1000), Account(account=ACT2021051200000002, amt=5000), Account(account=ACT2021051200000003, amt=1010)])
CreateAccountVO(visitKey=326D1478E3914C8A, user=null, accounts=[Account(account=ACT2021051200000001, amt=1000), Account(account=ACT2021051200000002, amt=5000)])// 执行3打印    
CreateAccountDTO(key=326D1478E3914C8A, accounts=[Account(account=ACT2021051200000001, amt=1000), Account(account=ACT2021051200000002, amt=5000), Account(account=ACT2021051200000003, amt=1010)])
CreateAccountVO(visitKey=326D1478E3914C8A, user=User(userName=Jaemon, addresses=[SZ, NS]), accounts=[Account(account=ACT2021051200000001, amt=1000), Account(account=ACT2021051200000002, amt=5000)])    

 

References

  • mapstruct官网
  • mapstruct github
  • cglib github
  • 使用cglib进行bean拷贝
  • 对象拷贝之Apache BeanUtils、Spring的BeanUtils、Mapstruct、BeanCopier、PropertieyUtils对比(深拷贝
  • 【教程】如何利用MapStruct 解决对象之间转换问题(一)

这篇关于使用MapStruct 解决对象之间转换、深拷贝问题的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1005353

相关文章

Conda与Python venv虚拟环境的区别与使用方法详解

《Conda与Pythonvenv虚拟环境的区别与使用方法详解》随着Python社区的成长,虚拟环境的概念和技术也在不断发展,:本文主要介绍Conda与Pythonvenv虚拟环境的区别与使用... 目录前言一、Conda 与 python venv 的核心区别1. Conda 的特点2. Python v

Spring Boot中WebSocket常用使用方法详解

《SpringBoot中WebSocket常用使用方法详解》本文从WebSocket的基础概念出发,详细介绍了SpringBoot集成WebSocket的步骤,并重点讲解了常用的使用方法,包括简单消... 目录一、WebSocket基础概念1.1 什么是WebSocket1.2 WebSocket与HTTP

C#中Guid类使用小结

《C#中Guid类使用小结》本文主要介绍了C#中Guid类用于生成和操作128位的唯一标识符,用于数据库主键及分布式系统,支持通过NewGuid、Parse等方法生成,感兴趣的可以了解一下... 目录前言一、什么是 Guid二、生成 Guid1. 使用 Guid.NewGuid() 方法2. 从字符串创建

Python使用python-can实现合并BLF文件

《Python使用python-can实现合并BLF文件》python-can库是Python生态中专注于CAN总线通信与数据处理的强大工具,本文将使用python-can为BLF文件合并提供高效灵活... 目录一、python-can 库:CAN 数据处理的利器二、BLF 文件合并核心代码解析1. 基础合

Python使用OpenCV实现获取视频时长的小工具

《Python使用OpenCV实现获取视频时长的小工具》在处理视频数据时,获取视频的时长是一项常见且基础的需求,本文将详细介绍如何使用Python和OpenCV获取视频时长,并对每一行代码进行深入解析... 目录一、代码实现二、代码解析1. 导入 OpenCV 库2. 定义获取视频时长的函数3. 打开视频文

SQL Server配置管理器无法打开的四种解决方法

《SQLServer配置管理器无法打开的四种解决方法》本文总结了SQLServer配置管理器无法打开的四种解决方法,文中通过图文示例介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的... 目录方法一:桌面图标进入方法二:运行窗口进入检查版本号对照表php方法三:查找文件路径方法四:检查 S

Spring IoC 容器的使用详解(最新整理)

《SpringIoC容器的使用详解(最新整理)》文章介绍了Spring框架中的应用分层思想与IoC容器原理,通过分层解耦业务逻辑、数据访问等模块,IoC容器利用@Component注解管理Bean... 目录1. 应用分层2. IoC 的介绍3. IoC 容器的使用3.1. bean 的存储3.2. 方法注

Python内置函数之classmethod函数使用详解

《Python内置函数之classmethod函数使用详解》:本文主要介绍Python内置函数之classmethod函数使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录1. 类方法定义与基本语法2. 类方法 vs 实例方法 vs 静态方法3. 核心特性与用法(1编程客

C# 比较两个list 之间元素差异的常用方法

《C#比较两个list之间元素差异的常用方法》:本文主要介绍C#比较两个list之间元素差异,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录1. 使用Except方法2. 使用Except的逆操作3. 使用LINQ的Join,GroupJoin

怎样通过分析GC日志来定位Java进程的内存问题

《怎样通过分析GC日志来定位Java进程的内存问题》:本文主要介绍怎样通过分析GC日志来定位Java进程的内存问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、GC 日志基础配置1. 启用详细 GC 日志2. 不同收集器的日志格式二、关键指标与分析维度1.