使用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

相关文章

Java使用Javassist动态生成HelloWorld类

《Java使用Javassist动态生成HelloWorld类》Javassist是一个非常强大的字节码操作和定义库,它允许开发者在运行时创建新的类或者修改现有的类,本文将简单介绍如何使用Javass... 目录1. Javassist简介2. 环境准备3. 动态生成HelloWorld类3.1 创建CtC

使用Python批量将.ncm格式的音频文件转换为.mp3格式的实战详解

《使用Python批量将.ncm格式的音频文件转换为.mp3格式的实战详解》本文详细介绍了如何使用Python通过ncmdump工具批量将.ncm音频转换为.mp3的步骤,包括安装、配置ffmpeg环... 目录1. 前言2. 安装 ncmdump3. 实现 .ncm 转 .mp34. 执行过程5. 执行结

Java实现将HTML文件与字符串转换为图片

《Java实现将HTML文件与字符串转换为图片》在Java开发中,我们经常会遇到将HTML内容转换为图片的需求,本文小编就来和大家详细讲讲如何使用FreeSpire.DocforJava库来实现这一功... 目录前言核心实现:html 转图片完整代码场景 1:转换本地 HTML 文件为图片场景 2:转换 H

Java使用jar命令配置服务器端口的完整指南

《Java使用jar命令配置服务器端口的完整指南》本文将详细介绍如何使用java-jar命令启动应用,并重点讲解如何配置服务器端口,同时提供一个实用的Web工具来简化这一过程,希望对大家有所帮助... 目录1. Java Jar文件简介1.1 什么是Jar文件1.2 创建可执行Jar文件2. 使用java

C#使用Spire.Doc for .NET实现HTML转Word的高效方案

《C#使用Spire.Docfor.NET实现HTML转Word的高效方案》在Web开发中,HTML内容的生成与处理是高频需求,然而,当用户需要将HTML页面或动态生成的HTML字符串转换为Wor... 目录引言一、html转Word的典型场景与挑战二、用 Spire.Doc 实现 HTML 转 Word1

Vue3绑定props默认值问题

《Vue3绑定props默认值问题》使用Vue3的defineProps配合TypeScript的interface定义props类型,并通过withDefaults设置默认值,使组件能安全访问传入的... 目录前言步骤步骤1:使用 defineProps 定义 Props步骤2:设置默认值总结前言使用T

Java中的抽象类与abstract 关键字使用详解

《Java中的抽象类与abstract关键字使用详解》:本文主要介绍Java中的抽象类与abstract关键字使用详解,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录一、抽象类的概念二、使用 abstract2.1 修饰类 => 抽象类2.2 修饰方法 => 抽象方法,没有

MyBatis ParameterHandler的具体使用

《MyBatisParameterHandler的具体使用》本文主要介绍了MyBatisParameterHandler的具体使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参... 目录一、概述二、源码1 关键属性2.setParameters3.TypeHandler1.TypeHa

Spring 中的切面与事务结合使用完整示例

《Spring中的切面与事务结合使用完整示例》本文给大家介绍Spring中的切面与事务结合使用完整示例,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考... 目录 一、前置知识:Spring AOP 与 事务的关系 事务本质上就是一个“切面”二、核心组件三、完

使用docker搭建嵌入式Linux开发环境

《使用docker搭建嵌入式Linux开发环境》本文主要介绍了使用docker搭建嵌入式Linux开发环境,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录1、前言2、安装docker3、编写容器管理脚本4、创建容器1、前言在日常开发全志、rk等不同