MapStruct 简介
在实际开发我们经常要在项目中进行 DTO、VO、DO、Entity 等实体之间的转换,这种类型的框架和工具类也有很多,比如 Spring 框架中的 BeanUtils 工具类 ,但这个工具类是通过反射来实现的,性能相对低下且存在类型转换异常的问题,所以这个工具在有些公司是禁止使用的。下面简单说一下MapStruct,MapStruct 是一个代码生成器,它基于约定优于配置的方法极大地简化了Java bean类型之间映射的实现。与其他映射框架相比,MapStruct在编译时生成Bean映射,以确保高性能,允许快速的开发人员反馈和彻底的错误检查。
MapStruct 官网 https://mapstruct.org/
技术比较
案例
一、新建 Maven 项目,并导入以下依赖
<!--mapStruct依赖-->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.4.1.Final</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId>
<version>1.4.1.Final</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.4.1.Final</version>
</dependency>
二、创建 entity、DTO、VO 类
1、创建 Member 实体类
public class Member {
private String id;
private String memberName;
private String password;
private String phone;
private String address;
// 省略getter setter
}
2、创建 MemberDTO
public class MemberDTO {
private String memberName;
private String phone;
private String address;
// 省略getter setter
}
3、创建 MemberVO
public class MemberVo {
private String id;
private String memberName;
// private String password;
private String phone;
private String address;
// 省略getter setter
}
三、定义 MapStruct 映射接口
/**
* @author wfd
* @description
* @create 2021/3/29 23:22
*/
@Mapper
public interface MemberMapstruct {
MemberMapstruct MEMBER_MAPSTRUCT = Mappers.getMapper(MemberMapstruct.class);
/**
* member 转为 memberVO
* @param member
* @return
*/
MemberVo memberToMemberVO(Member member);
/**
* memberVo 转为 member
* @param memberVo
* @return
*/
Member MemberVOToMember(MemberVo memberVo);
/**
* memberVo 转为 MemberDto
* @param memberVo
* @return
*/
MemberDTO memberVoToMemberDto(MemberVo memberVo);
}
四、测试
public class MapStructTest {
public static void main(String[] args) {
Member member = new Member();
member.setMemberName("wfd");
member.setPassword("ojd");
member.setId("123456");
member.setPhone("13400000000");
member.setAddress("gz");
/**
* member 转为 memberVO
*/
MemberVo memberVo = MemberMapstruct.MEMBER_MAPSTRUCT.memberToMemberVO(member);
System.out.println("memberVo = " + memberVo);
/**
* memberVo 转为 MemberDto
*/
MemberDTO memberDTO = MemberMapstruct.MEMBER_MAPSTRUCT.memberVoToMemberDto(memberVo);
System.out.println("memberDTO = " + memberDTO);
}
}
测试结果
memberVo = MemberVo{id='123456', memberName='wfd', phone='13400000000', address='gz'}
memberDTO = MemberDto{memberName='wfd', phone='13400000000', address='gz'}
可以看到项目编译的时候,自动生成了 MemberMapStructImpl这个实现类,打开这个实现类可以看到,实体之间在转换时会先新建一个实例对象,然后再通过setter方法进行赋值,并不是通过反射进行赋值,这也是为什么MapStruct的性能相对较好的原因。
总结
看完这篇文章可能有些小伙伴会感到困惑,MapStruct 进行实体转换的时候,如果有多个DTO、VO、Entity 进行转换的话,那么就要定义对应数量的方法才行,这样似乎看起来变得更麻烦了,直接使用 getter 和 setter 或者使用 BeanUtils 岂不是更为方便了 ?还不用引入 MapStruct 的 相关概念。其实在进行技术选型的时候,得配合应用场景来使用,如果实体的字段比较少,固然直接使用 getter setter 方法最为方便,但是当你的字段过多,一个实体七八十个字段的时候,那肯定是使用这些映射框架比较方便,且代码比较简洁。所以得具体问题,具体分析啦!!!
学习过程中遇到的问题
编译顺序问题
1、mapstruct 和 lombok 编译顺序问题,报以下错误
java: You aren't using a compiler supported by lombok, so lombok will not work and has been disabled.
Your processor is: com.sun.proxy.$Proxy26
Lombok supports: OpenJDK javac, ECJ
2、原因:使用lombok后,project在编译时,会生成包含了getter与setter的Source code.
使用mapstruct后,project在编译时,会生成mapper的具体实现类
mapstruct与lombok一起使用使时有时候会有到编译顺序的问题
当lombok的Source Code 未生成时,生成的mapper实现,将会缺少很多属性字段
3、解决方案:修改 maven 编译时的执行顺序
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>8</source>
<target>8</target>
<compilerArgs>--enable-preview</compilerArgs>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.4.1.Final</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
缓存问题
因为MapStruct在编译时期就将代码生成,如果我们一开始的文件名是 AA ,后来因为其他的原因改为了 BB,那么之前生成 AA 文件还是会保存在 target目录下的,这样会出现一种情况,就是你在自己本地可以跑项目,但是上了服务可能就会产生 文件找不到的异常,所以建议只要是修改了Mapstruct 相关的,就先 clean 一下,再重新编译