Java对象复制系列五: 最快速度、方便的对象复制工具 Mapper Struct 的高阶应用

👆🏻👆🏻👆🏻关注博主,让你的代码变得更加优雅。

前言

Mapper Struct 是目前最好的 Java 对象复制的工具之一。

上一节我们讲到了 Mapper Struct 的应用,以及测试了他的性能,发现他的性能已经无限接近直接 get/set 了。

今天我们来讲一下如何集成spring 以及一些高阶用法。让你在工作中各种对象转换游刃有余。

最佳实践

直接上案例

案例地址GitHub: https://github.com/zhuangjiaju/easytools/blob/main/easytools-web/easytools-web-web/src/main/java/com/github/zhuangjiaju/easytools/web/web/contoller/mapperstruct/MapperStructWebController.java

案例地址gitee: https://gitee.com/zhuangjiaju/easytools/blob/main/easytools-web/easytools-web-web/src/main/java/com/github/zhuangjiaju/easytools/web/web/contoller/mapperstruct/MapperStructWebController.java

先看完整代码

先看我们的对象转换需求:

  1. 将一个 json 字符串 转成一个数组
  2. 将一个 用户的DTO对象转成VO对象
  3. 放入一个当前的日期
  4. 放入一个唯一id

转换前的对象:

/*** MapperStruct 对象** @author Jiaju Zhuang*/
@Data
@SuperBuilder
@AllArgsConstructor
@NoArgsConstructor
public class MapperStructDTO implements Serializable {private static final long serialVersionUID = EasyToolsConstant.SERIAL_VERSION_UID;/*** id*/private Long id;/*** 用户id的json*/private String userIdJson;/*** 创建人*/private UserDTO createUser;
}

转换后的对象:

/*** 结果查询返回** @author Jiaju Zhuang*/
@Data
@SuperBuilder
@AllArgsConstructor
@NoArgsConstructor
public class MapperStructQueryVO implements Serializable {private static final long serialVersionUID = EasyToolsConstant.SERIAL_VERSION_UID;/*** id*/private Long id;/*** 用户id的json*/private List<Long> userIdList;/*** 创建人*/private UserVO createUser;/*** 当前日期*/private Date currentDate;/*** 唯一id*/private String uuid;
}

转换器代码:

/*** 结果查询返回** @author Jiaju Zhuang*/
@Mapper(componentModel = "spring", uses = {CommonWebMapper.class}, imports = {DateUtil.class})
public abstract class MapperStructWebMapper extends BaseCommonWebMapper {/*** 转换** @param user* @return*/@Mappings({@Mapping(target = "currentDate", expression = "java(DateUtil.date())"),@Mapping(target = "userIdList", source = "userIdJson", qualifiedByName = "commonJsonString2LongList"),})public abstract MapperStructQueryVO dto2vo(MapperStructDTO user);@AfterMappingprotected void afterMapperStructQuery(@MappingTarget MapperStructQueryVO target, MapperStructDTO source) {if (target == null) {return;}target.setUuid(UUID.fastUUID().toString());}
}

转换代码:


/*** 查询一条数据** @param id* @return*/
@GetMapping("query")
public DataResult<MapperStructQueryVO> query(@RequestParam("id") Long id) {MapperStructDTO data = mapperStructDemoService.queryExistent(id);// 转换需求如下:// 1. 将一个 json 字符串 转成一个数组// 2. 将一个 用户的DTO对象转成VO对象// 3. 放入一个当前的日期// 4. 放入一个唯一idMapperStructQueryVO vo = mapperStructWebMapper.dto2vo(data);log.info("转换前:{},转换后:{}", JSON.toJSONString(data), JSON.toJSONString(vo));return DataResult.of(vo);
}

输出结果:

转换前:{"createUser":{"id":1,"name":"Jiaju Zhuang"},"id":11,"userIdJson":"[\"1\",\"2\"]"},转换后:{"createUser":{"id":1,"name":"Jiaju Zhuang"},"currentDate":"2024-07-17 22:06:23.038","id":11,"userIdList":[1,2],"uuid":"eee3d4ab-bb8c-4e4c-95c5-4317a014c31a"}

返回给前端的结果:

{"success": true,"errorCode": null,"errorMessage": null,"data": {"id": 11,"userIdList": [1,2],"createUser": {"id": 1,"name": "Jiaju Zhuang"},"currentDate": "2024-07-17T14:06:23.038+00:00","uuid": "eee3d4ab-bb8c-4e4c-95c5-4317a014c31a"},"traceId": null
}

可以看到返回给前端的已经是我们想要的结果了,接下来我们一步步分解。

怎么集成spring?

在转换器上面加入 @Mapper(componentModel = “spring”) ,并且 componentModel 设置成 spring即可。 这样子 MapperStruct
就会把这个类注入到spring容器中。


@Mapper(componentModel = "spring", uses = {CommonWebMapper.class}, imports = {DateUtil.class})
public abstract class MapperStructWebMapper extends BaseCommonWebMapper {
}

然后需要的用的地方直接注入即可,我们这里用了构造器注入,大家也可以用 @Resource 注入:


@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/web/mapper-struct")
public class MapperStructWebController {private final MapperStructWebMapper mapperStructWebMapper;
}

如何将一个 json 字符串 转成一个数组?

由于代码中经常会有这个需求,所以我们把他抽到一个通用的mapper 工具里面去。

/*** 通用的web转换器** @author Jiaju Zhuang*/@Mapper(componentModel = "spring")
public abstract class CommonWebMapper {/*** 将一个json 数组转成** @param jsonString* @return*/@Named("commonJsonString2LongList")public List<Long> commonJsonString2LongList(String jsonString) {if (StringUtils.isBlank(jsonString)) {return null;}return JSON.parseArray(jsonString, Long.class);}
}

然后在 对象转换类里面加入 uses = {CommonWebMapper.class} 引入通用工具,最后用 qualifiedByName = "
commonJsonString2LongList" 调用我们指定的方法


@Mapper(componentModel = "spring", uses = {CommonWebMapper.class}, imports = {DateUtil.class})
public abstract class MapperStructWebMapper extends BaseCommonWebMapper {/*** 转换** @param user* @return*/@Mappings({@Mapping(target = "currentDate", expression = "java(DateUtil.date())"),@Mapping(target = "userIdList", source = "userIdJson", qualifiedByName = "commonJsonString2LongList"),})public abstract MapperStructQueryVO dto2vo(MapperStructDTO user);
}

如何将一个特别常用的DTO转成VO

当然也可以用上面的方法,这里我们介绍另外一种方法,继承。我们的转换器继承了 BaseCommonWebMapper ,当 转换对象中 遇到了
UserDTO 转成 UserVO 的时候,他自己会去父类里面找有没有这个方法。


/*** 结果查询返回** @author Jiaju Zhuang*/
@Mapper(componentModel = "spring")
public abstract class BaseCommonWebMapper {/*** 用户对象转成vo** @param user* @return*/public abstract UserVO _userDto2UserVo(UserDTO user);
}

转换器中继承 BaseCommonWebMapper 即可:


@Mapper(componentModel = "spring", uses = {CommonWebMapper.class}, imports = {DateUtil.class})
public abstract class MapperStructWebMapper extends BaseCommonWebMapper {/*** 转换** @param user* @return*/@Mappings({@Mapping(target = "currentDate", expression = "java(DateUtil.date())"),@Mapping(target = "userIdList", source = "userIdJson", qualifiedByName = "commonJsonString2LongList"),})public abstract MapperStructQueryVO dto2vo(MapperStructDTO user);
}

如何在转换的时候自己写代码

这个特别简单,使用 expression 即可,这样子我们就可以自己在表达式里面写代码了。当然要注意,如果遇到了方法不在当前类,需要使用
imports = {DateUtil.class} 引入该类。


@Mapper(componentModel = "spring", uses = {CommonWebMapper.class}, imports = {DateUtil.class})
public abstract class MapperStructWebMapper extends BaseCommonWebMapper {/*** 转换** @param user* @return*/@Mappings({@Mapping(target = "currentDate", expression = "java(DateUtil.date())"),@Mapping(target = "userIdList", source = "userIdJson", qualifiedByName = "commonJsonString2LongList"),})public abstract MapperStructQueryVO dto2vo(MapperStructDTO user);
}

如何写特别复杂的逻辑代码

特别复杂的逻辑代码可以在转换器转换完以后,使用 @AfterMapping 注解,然后在这个方法里面写任何代码。 当内容转换完成以后,
MapperStruct 会自动执行这个方法。

/*** 转换器** @author Jiaju Zhuang*/
@Mapper(componentModel = "spring", uses = {CommonWebMapper.class}, imports = {DateUtil.class})
public abstract class MapperStructWebMapper extends BaseCommonWebMapper {/*** 转换** @param user* @return*/@Mappings({@Mapping(target = "currentDate", expression = "java(DateUtil.date())"),@Mapping(target = "userIdList", source = "userIdJson", qualifiedByName = "commonJsonString2LongList"),})public abstract MapperStructQueryVO dto2vo(MapperStructDTO user);@AfterMappingprotected void afterMapperStructQuery(@MappingTarget MapperStructQueryVO target, MapperStructDTO source) {if (target == null) {return;}target.setUuid(UUID.fastUUID().toString());}
}

总结

今天给大家介绍了各种高阶的使用方法,平时工作应该完全胜任了,代码会别的特别优雅,把复杂的逻辑写到转换器里面,代码会变得特别优雅。

那么问题来了,MapperStruct 究竟为什么可以做到这么高速,而且可以实现这么复杂的转换呢?

带着疑问,我们下一节再见。

写在最后

给大家推荐一个非常完整的Java项目搭建的最佳实践,也是本文的源码出处,由大厂程序员&EasyExcel作者维护。
github地址:https://github.com/zhuangjiaju/easytools
gitee地址:https://gitee.com/zhuangjiaju/easytools

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/47329.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

云监控(华为) | 实训学习day1(10)

云监控&#xff1a;确保服务器高效运行 在当今的数字化时代&#xff0c;服务器的稳定运行对于任何企业都至关重要。为了确保服务器的 CPU、内存和硬盘等资源的合理运行&#xff0c;云监控成为了一项不可或缺的技术。本文将详细介绍云监控的基本概念、所需软件、配置方法以及如何…

Spring Cloud Gateway 响应数据加密

Spring Cloud Gateway实现了请求的解密和响应的加密&#xff0c;‌主要使用的是Hutool工具类中的AES加密算法。‌这种加密方式不仅用于请求数据的解密&#xff0c;‌还用于响应数据的加密。‌通过在网关中添加过滤器GlobalFilter&#xff0c;‌可以实现这一功能。‌具体来说&am…

mysql的存储函数FUNCTION详解(定义、语法、创建、调用、和存储过程的异同)

目录 一、定义 二、存储函数的特点 1. 封装性 2. 重用性 3. 性能优化 4. 安全性 三、创建存储函数 1、基本语法 2、解释 四、存储函数示例 1、创建 2、调用存储函数 五、使用存储函数 1、使用方法 2、修改和删除存储函数 六、和存储过程PROCEDURE的区别 1. 返…

JDBC 技术 | Java连接MySQL数据库(四万字零基础保姆级超全详解)

文章目录 前言一. JDBC概述1. JDBC 概念2. JDBC 本质3. JDBC 的好处 二. JDBC 快速入门1. 编写Java 程序步骤2. 在IDEA 中的操作流程3. 正式编写 Java程序 三. JDBC API详解1. DriverManager 类2. Connection 接口2.1 获取执行SQL语句的对象 3 .ResultSet 类3.1 概述3.2 代码实…

【MySQL】10.用户管理

用户管理 一.用户1.创建用户2.删除用户3.修改用户的密码 二.用户对数据库的权限1.给用户授权2.查看用户权限3.回收用户权限 一.用户 所有的用户信息都保存在数据库 mysql 的 user 表中 select host, user, authentication_string from user;----------------------------------…

sql优化策略

1.索引优化 在经常用于过滤&#xff08;WHERE子句&#xff09;和排序&#xff08;ORDER BY子句&#xff09;的列上创建索引。(使用索引) 避免在WHERE子句中使用非等值比较&#xff08;如!, <>&#xff09;&#xff0c;因为这可能使数据库无法使用索引。 使用覆盖索引&a…

Cadence23学习笔记(四)

这个人讲cadence也很不错&#xff1a; 73、创建Power NetClass[Cadence Allegro132讲视频教程字幕版]_哔哩哔哩_bilibili 上位机开发&#xff1a; MFC 最详细入门教程-CSDN博客 Board Geometry — Design_Outline 板框 Etch — Top 走线 Pin — Top 焊盘 …

06day-C++类和对象(下)

这里写目录标题 1. 再探构造函数小例题1 2. 类型转换3. static成员小例题2 4. 友元5. 内部类6. 匿名对象7.对象拷⻉时的编译器优化 1. 再探构造函数 • 之前我们实现构造函数时&#xff0c;初始化成员变量主要使⽤函数体内赋值&#xff0c;构造函数初始化还有⼀种⽅式&#xf…

探索APP开发中的主流版式设计与应用实践

在当今移动互联网高速发展的时代&#xff0c;APP已成为人们日常生活中不可或缺的一部分。无论是社交娱乐、购物支付还是工作学习&#xff0c;各类APP都以其独特的界面设计和用户体验赢得了用户的青睐。而APP开发的版式设计和页面规范&#xff0c;则是决定用户体验好坏的关键因素…

记录些MySQL题集(6)

MySQL 单表为什么不要超过 2000W 行&#xff1f; 数据持久化在磁盘中&#xff0c;磁盘的最小单元是扇区&#xff0c;一个扇区 0.5 KB&#xff0c;而由 8 个扇区可以构成一个文件系统块&#xff08;4K&#xff09;&#xff0c;以 InnoDB 存储引擎为例&#xff0c;一个数据页的大…

Netty TCP

Netty在TCP通信中扮演着重要的角色&#xff0c;它是一个高性能、异步事件驱动的网络应用框架&#xff0c;专门用于快速开发可维护的高性能协议服务器和客户端。以下是从不同方面对Netty在TCP通信中的应用进行详细说明&#xff1a; 一、Netty的特点与优势 高性能&#xff1a;N…

打卡第15天------二叉树

最近公司给我派活儿太多了,要干好多活儿,好多工作任务要处理,我都没时间刷题了。leetcode上的题目通过数量一直停留在原地不动,我真的很着急呀,我现在每天过的都有一种紧迫感,很着急,有一种与时间赛跑的感觉,真的时间过的太快了,没有任何人能够阻挡住时间的年轮向前推…

【异步爬虫:利用异步协程抓取一部电影】

利用异步协程抓取一部电影 我们把目光转向wbdy. 目前该案例还是可以用的. 我们想要抓取网上的视频资源就必须要了解我们的视频网站是如何工作的. 这里我用91看剧来做举例. 其他网站的原理是一样的. 1.视频网站是如何工作的 假设, 你现在想要做一个视频网站. 也有很多的UP猪…

【BUG】已解决:java.lang.IllegalStateException: Duplicate key

已解决&#xff1a;java.lang.IllegalStateException: Duplicate key 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博主英杰&#xff0c;211科班出身&#xff0c;就职于医疗科技公司&#xff0c;热衷分享知识&#xff0c;武汉城市…

【数学建模】——多领域资源优化中的创新应用-六大经典问题解答

目录 题目1&#xff1a;截取条材 题目 1.1问题描述 1.2 数学模型 1.3 求解 1.4 解答 题目2&#xff1a;商店进货销售计划 题目 2.1 问题描述 2.2 数学模型 2.3 求解 2.4 解答 题目3&#xff1a;货船装载问题 题目 3.1问题重述 3.2 数学模型 3.3 求解 3.4 解…

常用网络术语或概念

1. IP地址&#xff08;IP Address&#xff09; IPv4地址&#xff1a; 一种32位地址&#xff0c;用于标识网络中的设备。通常表示为四个十进制数&#xff0c;每个数用点分隔&#xff0c;例如192.168.1.1。    IPv6地址&#xff1a; 一种128位地址&#xff0c;用于解决IPv4地址…

【Go系列】 Go的高并发模式

承上启下 我们在之前已经学习了goroutine和channel的并发模式&#xff0c;也学会了sync库和context的控制。那么在Go里面一般都会使用哪些高并发模式呢&#xff1f;今天让我们在这篇文章中一起揭晓一下。 开始学习 for ... select...模式 for select模式是Go语言中处理并发的…

tomcat如何进行调优?

从两个方面考虑&#xff1a;内存和线程 首先启动Tomcat&#xff0c;实际上就是启动了一个JVM&#xff0c;所以可以按JVM调优的方式来进行调整&#xff0c;从而达到Tomcat优化的目的。 另外Tomcat中设计了一些缓存区&#xff0c;比如appReadBufSize、bufferPoolSize等缓存区来提…

设计模式使用场景实现示例及优缺点(结构型模式——享元模式)

国度的东南角&#xff0c;有一个被称为“享元村”的小村庄。村里的居民都是非常聪明的软件设计师&#xff0c;他们擅长用一种叫做“享元模式”的技术来解决内存使用问题。享元模式的核心思想是共享&#xff1a;通过共享来支持大量的细粒度对象的使用&#xff0c;从而在不牺牲程…

C# 设计一个可变长度的数据通信协议编码和解码代码。

设计一个可变长度的数据通信协议编码和解码代码。 要有本机ID字段&#xff0c;远端设备ID字段&#xff0c;指令类型字段&#xff0c;数据体字段&#xff0c;校验字段。其中一个要求是&#xff0c;每次固定收发八个字节&#xff0c;单个数据帧超过八个字节需要分包收发。对接收的…