定义一个dto对象_业务代码的救星——Java 对象转换框架 MapStruct 妙用

1374143aa4877b82d86b045be9c080d8.png

在业务项目的开发中,我们经常需要将 Java 对象进行转换,比如从将外部微服务得到的对象转换为本域的业务对象 domainobject,将 domainobject 转为数据持久层的 dataobject,将 domainobject 转换为 DTO 以便返回给外部调用方等。在转换时大部分属性都是相同的,只有少部分的不同,如果手工编写转换代码,会很繁琐。这时我们可以通过一些对象转换框架来更方便的做这件事情。

这样的对象转换框架有不少,比较有名的有 ModelMapper 和 MapStruct。它们所使用的实现技术不同,ModelMapper 是基于反射的,通过反射来查找实体对象的字段,并读取或写入值,这样的方式实现原理简单,但性能很差。与 ModelMapper 框架不同的是,MapStruct 是基于编译阶段代码生成的,生成的转换代码在运行的时候跟一般的代码一样,没有额外的性能损失。本文重点介绍 MapStruct。

业务场景

假设现在有这么个场景,从数据库查询出来了一个 user 对象(包含 id,用户名,密码,手机号,邮箱,角色这些字段)和一个对应的角色对象 role(包含 id,角色名,角色描述这些字段),现在在 controller 需要用到 user 对象的 id,用户名,和角色对象的角色名三个属性。一种方式是直接把两个对象传递到 controller 层,但是这样会多出很多没用的属性。更通用的方式是需要用到的属性封装成一个类(DTO),通过传输这个类的实例来完成数据传输。

实现方式之使用传统方式

如下:

User.java@AllArgsConstructor
@Data
public class User {private Long id;private String username;private String password;private String phoneNum;private String email;private Role role;
}
Role.java@AllArgsConstructor
@Data
public class Role {private Long id;private String roleName;private String description;
}
UserRoleDto.java
@Data
public class UserRoleDto {/*** 用户id*/private Long userId;/*** 用户名*/private String name;/*** 角色名*/private String roleName;
}
MainTest.java
测试类,模拟将 user 对象转换成 UserRoleDto 对象
public class MainTest {User user = null;/*** 模拟从数据库中查出 user 对象*/@Beforepublic void before() {Role role  = new Role(2L, "administrator", "超级管理员");user  = new User(1L, "zhangsan", "12345", "17677778888", "123@qq.com", role);}/*** 模拟把 user 对象转换成 UserRoleDto 对象*/@Testpublic void test1() {UserRoleDto userRoleDto = new UserRoleDto();userRoleDto.setUserId(user.getId());userRoleDto.setName(user.getUsername());userRoleDto.setRoleName(user.getRole().getRoleName());System.out.println(userRoleDto);}
}

运行结果

10554362c510acc160227027e8b2f7d2.png

上边的代码或许暂时看起来还是比较简洁的,但是我们需要注意的一点就是平时业务开发中的对象属性远不是上述代码中简简单单的几个字段,有可能会有数十个字段,同理也会数十个对象需要转换,我们如果还是通过 getter、setter 的方式把一个对象属性值复制到另一个对象中去还是非常麻烦的,不过不用担心,今天要介绍给大家的 MapStruct 就是用于解决这种问题的。

实现方式之使用 MapStruct

这里我们沿用上述代码中的基本对象 User.javaRole.javaUserRoleDto.java。然后新建一个 UserRoleMapper.java,这个来用来定义 User.javaRole.javaUserRoleDto.java之间属性对应规则。

在这之前我们需要引入 MapStruct 的 pom 引用:

 <dependency><groupId>org.mapstruct</groupId><artifactId>mapstruct-jdk8</artifactId><version>1.3.0.Final</version>
</dependency>
UserRoleMapper.java
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;/*** @Mapper 定义这是一个MapStruct对象属性转换接口,在这个类里面规定转换规则*         在项目构建时,会自动生成改接口的实现类,这个实现类将实现对象属性值复制*/
@Mapper
public interface UserRoleMapper {/*** 获取该类自动生成的实现类的实例* 接口中的属性都是 public static final 的* 方法都是public abstract 的*/UserRoleMapper INSTANCES = Mappers.getMapper(UserRoleMapper.class);/*** 这个方法就是用于实现对象属性复制的方法** @Mapping 用来定义属性复制规则*              source 指定源对象属性*              target 指定目标对象属性** @param user 这个参数就是源对象,也就是需要被复制的对象* @return 返回的是目标对象,就是最终的结果对象*/@Mappings({@Mapping(source = "id", target = "userId"),@Mapping(source = "username", target = "name"),@Mapping(source = "role.roleName", target = "roleName")})UserRoleDto toUserRoleDto(User user);}

测试一下结果

MainTest.java
/*** 模拟通过MapStruct把user对象转换成UserRoleDto对象*/@Testpublic void test2() {UserRoleDto userRoleDto = UserRoleMapper.INSTANCES.toUserRoleDto(user);System.out.println(userRoleDto);}

呃,很明显,运行竟然报错了,具体异常如下:

2a60a3fade0d84ce4f0e8ddfe030ed42.png

核心是这一句 :java.lang.ClassNotFoundException:Cannotfind implementationfortop.zhoudl.mapstruct.UserRoleMapper ,也就是说没有找到 UserRoleMapper 类的实现类。

通过查阅一些资料可得:

MapStruct 是一个可以处理注解的Java编译器插件,可以在命令行中使用,也可以在 IDE 中使用。MapStruc t有一些默认配置,但是也为用户提供了自己进行配置的途径。缺点就是这玩意在使用工具自带的编译器时不会生成实现类,需要通过 maven 的方式来进行编译,然后才会生成实现类。

所以我们需要增加一个编译插件到 pom 文件中:

<!-- 引入 processor -->
<dependency><groupId>org.mapstruct</groupId><artifactId>mapstruct-processor</artifactId><version>1.3.0.Final</version><scope>provided</scope>
</dependency>
<!--为 Maven compile plugin 设置 annotation processor -->
<plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.5.1</version><configuration><source>1.8</source><target>1.8</target><annotationProcessorPaths><path><groupId>org.mapstruct</groupId><artifactId>mapstruct-processor</artifactId><version>1.2.0.Final</version></path></annotationProcessorPaths></configuration></plugin>
</plugins>

然后我们运行程序就可以得到自己想要的结果了

10554362c510acc160227027e8b2f7d2.png

安装 MapStruct 插件

使用 MapStruct,还有一个缺点就是,当属性改名的时候,因为在 Mapper 上注解中配置的名字是在字符串里面,因此不会自动同步的。所以 MapStruct 提供了一个插件来解决这个问题,同时还提供代码自动提示、点击跳转到实现等功能。

关于插件的更多信息,参见 MapStruct support for IntelliJ IDEA

安装插件的过程

在 IDEA 中依次打开 File - > Settings - > Plugins

然后在 Markeyplace 搜索框中输入 mapstruct,点击 install,然后重启 IDE 即可。

221a0c876820879d4c286220c1fa1c0a.png

一些可能会出现的问题

  • 找不到注释处理程序:在 pom.xml 中增加 mapstruct-processor 的依赖
  • 没有找到实现类:在 pom.xml 中加入对 mapstruct-processor 的依赖
  • 在 IDEA 里面 enable Annotation Processor
  • 使用 Lombok 的情况下,编译时报 Data 类的 setter/getter 找不到:把 lombok 加入到annotationProcessorPath,如下图

138b04304037c49cc957679be43b1d06.png

总结

MapSturct 是一个生成类型安全, 高性能且无依赖的 JavaBean 映射代码的注解处理器(annotation processor)。

作为一个注解处理器, 通过 MapStruct 生成的代码具有怎么样的优势呢?抓一下重点:

  1. 注解处理器
  2. 可以生成 JavaBean 之间的映射代码
  3. 类型安全, 高性能, 无依赖性

高性能

这是相对反射来说的, 反射需要去读取字节码的内容, 花销会比较大。而通过 MapStruct来生成的代码, 其类似于人手写,代码执行速度上可以得到保证。(前面例子中生成的代码可以在编译后看到,在项目的 target/generated-sources/annotations 目录里可以看到具体代码)。

易于 debug

在我们生成的代码中, 我们可以轻易的进行 debug。但是如果是使用反射实现代码的时候, 一旦出现了问题, 很多时候是比较难找到原因。

使用相对简单

如果是完全映射的, 使用起来肯定没有反射简单。用类似 BeanUtils 这些工具一条语句就搞定了。但是,如果需要进行特殊的匹配(特殊类型转换, 多对一转换等), MapStruct 的优势就比较明显了,基本上我们只需要在使用的时候声明一个接口, 接口下写对应的方法, 就可以使用了(当然, 如果有特殊情况, 是需要额处理一下的)。

代码独立

生成的代码是对立的, 没有运行时的依赖


原作者:zhoudl
原文链接:业务代码的救星——Java 对象转换框架 MapStruct 妙用
原出处:公众号

26b63fa09423e8fdc2c26c146af8630d.gif

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

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

相关文章

discuz设置用户每天回帖数_[建站教程]Discuz3.4设置QQ互联登陆教程

虽然现在很多人已经不在使用QQ了&#xff0c;但瘦死的骆驼比马大&#xff0c;QQ的用户基数还是很大&#xff0c;而且QQ里有大量的年轻用户&#xff0c;像我的表妹&#xff0c;表弟刚上初中。他们是忠诚的QQ用户。为了获取这批年轻的用户&#xff0c;我们还是有必要让网站支持QQ…

16进制加法 keil_C/C++编程笔记:C语言进制详解,二进制、八进制和十六进制

我们平时使用的数字都是由 0~9 共十个数字组成的&#xff0c;例如 1、9、10、297、952 等&#xff0c;一个数字最多能表示九&#xff0c;如果要表示十、十一、二十九、一百等&#xff0c;就需要多个数字组合起来。例如表示 58 的结果&#xff0c;一个数字不够&#xff0c;只能”…

MySQL的索引是什么?怎么优化?

索引类似大学图书馆建书目索引&#xff0c;可以提高数据检索的效率&#xff0c;降低数据库的IO成本。MySQL在300万条记录左右性能开始逐渐下降&#xff0c;虽然官方文档说500~800w记录&#xff0c;所以大数据量建立索引是非常有必要的。MySQL提供了Explain&#xff0c;用于显示…

git实现审核功能_一文教你如何搭建PDD分佣小程序实现财富自由

随着拼多多的火爆&#xff0c;很多淘客以各种方式通过推广拼多多商品获取返佣来月入万元&#xff0c;实现财富自由。只要你有流量或者足够努力&#xff0c;像其他淘客一样实现睡后过万财富自由不是梦。本文通过详细教程教你快速搭建属于自己的PDD分佣小程序&#xff0c;完成自己…

9型转x型 cobol_兰州一餐馆推鸳鸯牛肉面 9种面型一面多吃

来源标题&#xff1a;兰州一餐馆推鸳鸯牛肉面&#xff0c;清汤酸菜各一边还有9种面型&#xff0c;网友&#xff1a;能连吃三碗近日&#xff0c;位于甘肃兰州的一家牛肉面馆推出了鸳鸯牛肉面。一个大碗分隔为两边&#xff0c;一边是传统清汤牛肉面&#xff0c;另一边是酸菜牛肉面…

【算法系列之十三】二叉树两叶节点的最大距离

1、题目描述 给定一棵二叉树&#xff0c;计算这课二叉树的直径长度&#xff0c;即为二叉树任意两个节点间的最长路径。比如&#xff1a; 这棵二叉树的最长路径为3。 2、解题思路 使用递归进行求解&#xff0c;每次递归的过程中&#xff0c;先求出以某个节点为树根的二…

date比较大小 mybatis_Hibernate 和 MyBatis 哪个更好用?

Java大联盟帮助万千Java学习者持续成长关注作者&#xff5c;SylvanasSun郑沐兴https://zhuanlan.zhihu.com/p/21966051B 站搜索&#xff1a;楠哥教你学Java获取更多优质视频教程前言由于编程思想与数据库的设计模式不同&#xff0c;生出了一些ORM框架。核心都是将关系型数据库和…

简单的cpu飙升排查方法

1先来一段飙升代码 public class FindJavaThreadInTaskManager {public static void main(String[] args) {Thread thread new Thread(new Worker());thread.start();}static class Worker implements Runnable {Overridepublic void run() {while (true) {System.out.printl…

tortoisesvn创建部署项目_FrameWork如何进行云托管部署

介绍CloudBase Framework 是云开发官方出品的云原生一体化部署工具&#xff0c;可以帮助开发者将静态网站、后端服务和小程序等应用&#xff0c;一键部署到云开发 Serverless 架构的云平台上&#xff0c;自动伸缩且无需关心运维&#xff0c;聚焦应用本身&#xff0c;无需关心底…

修改背景图片_我花了5小时,为网易修改了一份内容超多的PPT,效果超级赞!!...

微信扫码观看全套Excel、Word、PPT视频作者&#xff1a;宋雪贤 来源&#xff1a;PPT进化论(ID&#xff1a;PPTjinhualun)哈喽&#xff0c;大家好&#xff0c;不知道您看过《我花了3个小时&#xff0c;为京东修改了一份PPT&#xff0c;效果好到惊人&#xff01;》这篇案例修改文…

MySQL千万级别大表如何优化?

当MySQL单表记录数过大时&#xff0c;增删改查性能都会急剧下降&#xff0c;可以参考以下步骤来优化&#xff1a; 单表优化 除非单表数据未来会一直不断上涨&#xff0c;否则不要一开始就考虑拆分&#xff0c;拆分会带来逻辑、部署、运维的各种复杂度&#xff0c;一般以整型值…

命令测试post_【第2088期】前端中台化,把格局做大——NodeJS 和测试服务探索

前言今日早读文章由《React状态管理与同构实战》作者LucasHC投稿分享。正文从这开始~~近些年&#xff0c;「NodeJS 应该如何在公司业务中真实落地 」这类问题屡见不鲜。自从 2009 年 NodeJS 诞生之后&#xff0c;抢尽风头&#xff0c;圈粉无数。但一定有工程师不禁要质疑「Node…

Java 诊断工具 Arthas 常见命令

基本概念 云原生这么多微服务&#xff0c;当然需要一个诊断利器来排查问题。 Arthas 是阿里开源的 Java 诊断工具&#xff0c;深受开发者喜爱。在线排查问题&#xff0c;无需重启&#xff1b;动态跟踪 Java 代码&#xff1b;实时监控 JVM 状态。Arthas 支持 JDK 6&#xff0c…

28和lba48命令格式区别_编译Sass(命令行)

本文作者&#xff1a;开课吧无忧图文编辑&#xff1a;开三金sass编译有很多种方式&#xff0c;如命令行编译模式、编辑器自动编译、编译软件koala、sass-loader等。今天我们就先来看第一种&#xff1a;命令行编译刚才我在test文件夹里面已经建立了一个style.scss文件&#xff0…

子窗体中组合框联动_一张表实现组合框联动

嗨&#xff0c;大家中午好&#xff01;最近&#xff0c;有网友给我私信&#xff0c;想要一个联动的示例&#xff0c;一个有关于部门联动的操作。其实关于联动的操作有很多&#xff0c;可以是组合框的联动&#xff0c;列表框联动&#xff0c;组合框与列表框也可以联动&#xff0…

中如何实现文字转语音_录音转文字、文字转语音,学会这一招就够了!手把手教你如何操作...

阅读文章时候想着有人可以把文章读给我听就好了&#xff0c;写作时想着语音直接可以转换成文字就好了&#xff0c;大家是不是有时会突然冒出这样的想法&#xff1f;七十这些看似天真的想法&#xff0c;还真的有办法解决&#xff0c;这里就手把手教你如何操作才能将的文字转换成…

图像 理想低通滤波_图像处理之滤波(下)

[toc]目录一、常规滤波低通高通带通带阻二、非局部均值滤波三、维纳滤波四、卡尔曼滤波前言所谓滤波&#xff0c;其实就是从混合在一起的诸多信号中提取出所需要的信号。信号的分类&#xff1a;确定型信号&#xff0c;可以表示为确定的时间函数&#xff0c;可确定其在任何时刻的…

泰山行宫碧霞元君祠_临清市泰山行宫碧霞元君祠5月4号(农历三月三十日)举行大型泰山奶奶接驾法会...

临清是泰山奶奶的娘家&#xff0c;每年的四月泰山奶奶要回临清省亲临清市道教协会定于2019年农历三月三十(5月4号星期六)于临清桑树园泰山行宫碧霞元君祠举行大型泰山奶奶迎鸾接驾庙会。届时&#xff0c;将有架鼓会、云龙会、狮胞会、钢叉会、高跷会、天音会、彩船会、秧歌会等…

充分条件反过来是必要条件吗_“充分必要条件”引发的现实思考

昨天看了一篇文章是介绍“充分条件和必要条件”&#xff0c;大致就是A能直接推导出B&#xff0c;那A就是B的充分条件。A不一定能推导出B&#xff0c;但是没A一定推导不出B&#xff0c;那A就是B必要条件。举个简单的例子&#xff1a;对你好&#xff08;A&#xff09;与喜欢你&am…

手机游戏降低游戏延迟的软件_怎么降低手机网络延迟(减少网络延迟的5个小技巧)...

在过去的几十年里&#xff0c;用户或开发人员并不担心延迟。在上世纪90年代和本世纪初&#xff0c;个人互联网连接速度要慢得多&#xff0c;因此发送请求和接收响应之间的延迟要远远小于下载完成所需的时间。如今&#xff0c;更高的带宽连接使下载速度更快&#xff0c;因此延迟…