平时做项目的时候,经常需要做PO、VO、DTO之间的转换。简单的对象转换,使用BeanUtils基本上是够了,但是复杂的转换,如果使用它的话又得写一堆Getter、Setter方法了。今天给大家推荐一款对象自动映射工具
MapStruct
,功能真心强大!
SpringBoot实战电商项目mall(50k+star)地址:github.com/macrozheng/…
关于BeanUtils
平时我经常使用Hutool中的BeanUtil类来实现对象转换,用多了之后就发现有些缺点:
- 对象属性映射使用反射来实现,性能比较低;
- 对于不同名称或不同类型的属性无法转换,还得单独写Getter、Setter方法;
- 对于嵌套的子对象也需要转换的情况,也得自行处理;
- 集合对象转换时,得使用循环,一个个拷贝。
对于这些不足,MapStruct都能解决,不愧为一款功能强大的对象映射工具!
MapStruct简介
MapStruct是一款基于Java注解的对象属性映射工具,在Github上已经有4.5K+Star。使用的时候我们只要在接口中定义好对象属性映射规则,它就能自动生成映射实现类,不使用反射,性能优秀,能实现各种复杂映射。
IDEA插件支持
作为一款非常流行的对象映射工具,MapStruct还提供了专门的IDEA插件,我们在使用之前可以先安装好插件。
项目集成
在SpingBoot中集成MapStruct非常简单,仅续添加如下两个依赖即可,这里使用的是
1.4.2.Final
版本。
1 | xml复制代码<dependency> |
基本使用
集成完MapStruct之后,我们来体验下它的功能吧,看看它有何神奇之处!
基本映射
我们先来个快速入门,体验一下MapStruct的基本功能,并聊聊它的实现原理。
- 首先我们准备好要使用的会员PO对象
Member
;
1 | java复制代码/** |
- 然后再准备好会员的DTO对象
MemberDto
,我们需要将Member
对象转换为MemberDto
对象;
1 | java复制代码/** |
- 然后创建一个映射接口
MemberMapper
,实现同名同类型属性、不同名称属性、不同类型属性的映射;
1 | java复制代码/** |
- 接下来在Controller中创建测试接口,直接通过接口中的
INSTANCE
实例调用转换方法toDto
;
1 | java复制代码/** |
- 运行项目后在Swagger中测试接口,发现PO所有属性已经成功转换到DTO中去了,Swagger访问地址:http://localhost:8088/swagger-ui
- 其实MapStruct的实现原理很简单,就是根据我们在Mapper接口中使用的
@Mapper
和@Mapping
等注解,在运行时生成接口的实现类,我们可以打开项目的target
目录看下;
- 下面是MapStruct为
MemberMapper
生成好的对象映射代码,可以和手写Getter、Setter说再见了!
1 | java复制代码public class MemberMapperImpl implements MemberMapper { |
集合映射
MapStruct也提供了集合映射的功能,可以直接将一个PO列表转换为一个DTO列表,再也不用一个个对象转换了!
- 在
MemberMapper
接口中添加toDtoList
方法用于列表转换;
1 | java复制代码/** |
- 在Controller中创建测试接口,直接通过Mapper接口中的
INSTANCE
实例调用转换方法toDtoList
;
1 | java复制代码/** |
- 在Swagger中调用接口测试下,PO列表已经转换为DTO列表了。
子对象映射
MapStruct对于对象中包含子对象也需要转换的情况也是有所支持的。
- 例如我们有一个订单PO对象
Order
,嵌套有Member
和Product
对象;
1 | java复制代码/** |
- 我们需要转换为
OrderDto
对象,OrderDto
中包含MemberDto
和ProductDto
两个子对象同样需要转换;
1 | java复制代码/** |
- 我们只需要创建一个Mapper接口,然后通过使用
uses
将子对象的转换Mapper注入进来,然后通过@Mapping
设置好属性映射规则即可;
1 | java复制代码/** |
- 接下来在Controller中创建测试接口,直接通过Mapper中的
INSTANCE
实例调用转换方法toDto
;
1 | java复制代码/** |
- 在Swagger中调用接口测试下,可以发现子对象属性已经被转换了。
合并映射
MapStruct也支持把多个对象属性映射到一个对象中去。
- 例如这里把
Member
和Order
的部分属性映射到MemberOrderDto
中去;
1 | java复制代码/** |
- 然后在Mapper中添加
toMemberOrderDto
方法,这里需要注意的是由于参数中具有两个属性,需要通过参数名称.属性
的名称来指定source
来防止冲突(这两个参数中都有id属性);
1 | java复制代码/** |
- 接下来在Controller中创建测试接口,直接通过Mapper中的
INSTANCE
实例调用转换方法toMemberOrderDto
;
1 | java复制代码/** |
- 在Swagger中调用接口测试下,可以发现Member和Order中的属性已经被映射到MemberOrderDto中去了。
进阶使用
通过上面的基本使用,大家已经可以玩转MapStruct了,下面我们再来介绍一些进阶的用法。
使用依赖注入
上面我们都是通过Mapper接口中的INSTANCE实例来调用方法的,在Spring中我们也是可以使用依赖注入的。
- 想要使用依赖注入,我们只要将
@Mapper
注解的componentModel
参数设置为spring
即可,这样在生成接口实现类时,MapperStruct会为其添加@Component
注解;
1 | java复制代码/** |
- 接下来在Controller中使用
@Autowired
注解注入即可使用;
1 | java复制代码/** |
- 在Swagger中调用接口测试下,可以发现与之前一样可以正常使用。
使用常量、默认值和表达式
使用MapStruct映射属性时,我们可以设置属性为常量或者默认值,也可以通过Java中的方法编写表达式来自动生成属性。
- 例如下面这个商品类Product对象;
1 | java复制代码/** |
- 我们想把Product转换为ProductDto对象,
id
属性设置为常量,count
设置默认值为1,productSn
设置为UUID生成;
1 | java复制代码/** |
- 创建
ProductMapper
接口,通过@Mapping
注解中的constant
、defaultValue
、expression
设置好映射规则;
1 | java复制代码/** |
- 接下来在Controller中创建测试接口,直接通过接口中的
INSTANCE
实例调用转换方法toDto
;
1 | java复制代码/** |
- 在Swagger中调用接口测试下,对象已经成功转换。
在映射前后进行自定义处理
MapStruct也支持在映射前后做一些自定义操作,类似AOP中的切面。
- 由于此时我们需要创建自定义处理方法,创建一个抽象类
ProductRoundMapper
,通过@BeforeMapping
注解自定义映射前操作,通过@AfterMapping
注解自定义映射后操作;
1 | java复制代码/** |
- 接下来在Controller中创建测试接口,直接通过Mapper中的
INSTANCE
实例调用转换方法toDto
;
1 | java复制代码/** |
- 在Swagger中调用接口测试下,可以发现已经应用了自定义操作。
处理映射异常
代码运行难免会出现异常,MapStruct也支持处理映射异常。
- 我们需要先创建一个自定义异常类;
1 | java复制代码/** |
- 然后创建一个验证类,当
price
设置小于0
时抛出我们自定义的异常;
1 | java复制代码/** |
- 之后我们通过
@Mapper
注解的uses
属性运用验证类;
1 | java复制代码/** |
- 然后在Controller中添加测试接口,设置
price
为-1
,此时在进行映射时会抛出异常;
1 | java复制代码/** |
- 在Swagger中调用接口测试下,发现运行日志中已经打印了自定义异常信息。
总结
通过上面对MapStruct的使用体验,我们可以发现MapStruct远比BeanUtils要强大。当我们想实现比较复杂的对象映射时,通过它可以省去写Getter、Setter方法的过程。 当然上面只是介绍了MapStruct的一些常用功能,它的功能远不止于此,感兴趣的朋友可以查看下官方文档。
参考资料
官方文档:mapstruct.org/documentati…
项目源码地址
本文 GitHub github.com/macrozheng/… 已经收录,欢迎大家Star!
本文转载自: 掘金