[Java] Mapstruct-Plus:基于MapStruct的、增强版的Java数据对象映射框架
概述: Mapstruct-Plus
简介
MapStructPlus
可能是最简单最强大的Java Bean转换工具
Mapstruct Plus
是Mapstruct
的增强工具,在Mapstruct
的基础上,实现了自动生成Mapper
接口的功能,并强化了部分功能,使 Java 类型转换更加便捷、优雅。Mapstruct Plus
内嵌Mapstruct
,和Mapstruct
完全兼容,如果之前已使用Mapstruct
,可以无缝替换依赖
- Author : 代码笔耕
- URL
Java数据对象转换工具的发展背景与发展过程
- 在现代企业级软件开发中,随着应用架构的复杂化,业务逻辑与数据模型的层次也日益增多。
VO(View Object)、DTO(Data Transfer Object)、PO(Persistent Object)、BO(Business Object)等对象层次的转换需求变得极为普遍。
传统方法如sh调用手动getter/setter
或使用Apache Commons BeanUtils
进行对象复制,虽然实现简单,但维护成本高且容易引入错误,性能也不尽如人意。
-
MapStruct
的出现,通过注解处理器在编译期生成高效的映射代码,极大地简化了对象之间的转换。 -
而
MapStruct Plus
则进一步增强了这一功能,特别是通过@AutoMapper
注解,使得双向对象转换变得更加轻松和直观。
深入解析 MapStruct Plus 的 @AutoMapper 注解及其对象映射机制
@AutoMapper
注解的核心概念
@AutoMapper
是MapStruct Plus
提供的一个关键注解,用于简化Java
Bean对象之间的转换任务。
通过在一个类上添加该注解,开发者无需手动编写复杂的映射逻辑,就能自动生成两个类之间的转换接口。
该注解的出现极大地减少了代码冗余,提升了代码的可维护性和可读性。
工作原理
@AutoMapper
注解的核心工作机制基于注解处理器(Annotation Processor
)。
- 在编译期,
MapStruct Plus
的注解处理器会扫描带有@AutoMapper
注解的类,自动生成两个类之间的映射代码。- 注解处理器的这种编译期处理方式保证了转换代码的高效性,避免了运行时的反射调用,从而提升了性能。
MapStruct Plus 的特性
-
与 MapStruct 完全兼容:MapStruct Plus 是对 MapStruct 的增强扩展。如果你在项目中已经使用了 MapStruct,可以直接替换相关依赖以引入 MapStruct Plus,无需对现有代码进行大幅度修改。
-
双向转换的单注解实现:通过一个 @AutoMapper 注解,就可以同时生成两个类之间的双向转换代码,减少了注解的使用频率和复杂度。
-
高效的编译期生成:所有的映射逻辑都在编译期生成,完全避免了运行时的性能损耗,相比于反射等动态调用方式,性能更高。
Maven 依赖
引入方式1 清爽版
<!-- data object convert | start -->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${mapstruct.version}</version>
</dependency>
<!-- mapstruct-processor | 避免报错: Caused by: java.lang.ClassNotFoundException: Cannot find implementation for com.daq.sdk.pojo.mapstruct.convert.CustomerBeanMapper -->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
</dependency>
<dependency>
<groupId>io.github.linpeilie</groupId>
<artifactId>mapstruct-plus</artifactId>
<version>${mapstruct-plus.version}</version>
</dependency>
<dependency>
<groupId>io.github.linpeilie</groupId>
<artifactId>mapstruct-plus-object-convert</artifactId>
<version>${mapstruct-plus.version}</version>
</dependency>
<!-- data object convert | end -->
mapstruct.version
:1.5.5.Final
mapstruct-plus.version
: 1.4.6spring-boot
: 3.3.5 (经项目验证过的配套版本,了解即可)spring
: 6.1.14 (经项目验证过的配套版本,了解即可)
引入方式2 SpringBoot适配版
<properties>
<java.version>19</java.version>
<mapstruct-plus.version>1.3.5</mapstruct-plus.version>
<lombok.version>1.18.30</lombok.version>
</properties>
<dependencies>
<dependency>
<groupId>io.github.linpeilie</groupId>
<artifactId>mapstruct-plus-spring-boot-starter</artifactId>
<version>${mapstruct-plus.version}</version>
</dependency>
<!-- knife4j 版本4.0.0——SpringBoot版本兼容性 -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi2-spring-boot-starter</artifactId>
<version>4.0.0</version>
<exclusions>
<exclusion>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<!-- maven-compiler-plugin作用:指定 Java 版本、设置编码方式 -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
<path>
<groupId>io.github.linpeilie</groupId>
<artifactId>mapstruct-plus-processor</artifactId>
<version>${mapstruct-plus.version}</version>
</path>
<!-- 将 Lombok 注解和 MapStruct 的映射注解进行绑定,使它们能够协同工作 -->
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>0.2.0</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
案例实践
案例:单个类与单个类之间的映射(User => UserDto)
依赖引入
假定引入本文所指的【SpringBoot版】的依赖
User / UserDto : 实体类
package com.example.mapstructplusdemo.model.entity;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.experimental.Accessors;
/**
* @Description : 实体类 User
*/
@ToString //lombok 注解
@Data //lombok 注解
@Accessors(chain = true) //lombok 注解
@AllArgsConstructor //lombok 注解
@NoArgsConstructor //lombok 注解
@AutoMapper(target = UserDto.class) // 重点 (MapStruct Plus 新增的注解)
public class User {
private String username;
private int age;
private boolean young;
}
@ToString
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@Data
public class UserDto {
private String username;
// 属性名称不同时.
@AutoMapping(target = "age")
private int ageB;
private boolean young;
}
测试类
package com.example.mapstructplusdemo;
import com.example.mapstructplusdemo.model.entity.User;
import com.example.mapstructplusdemo.model.entity.UserDto;
import io.github.linpeilie.Converter;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class MapstructPlusDemoApplicationTests {
@Autowired
private Converter converter;
@Test
void contextLoads() {
User user = new User()
.setUsername("jack")
.setAge(23)
.setYoung(false);
UserDto userDto = converter.convert(user, UserDto.class);
System.out.println("userDto: "+ userDto);
/**
* 断言语句作用:验证代码的正确性。当断言失败时,会抛出AssertionError异常,
* 提醒我们假设不成立,需修正。
* 上线时,需删除,以提高代码的执行效率。
*/
assert user.getUsername().equals(userDto.getUsername());
assert user.getAge() == userDto.getAge();
assert user.isYoung() == userDto.isYoung();
User newUser = converter.convert(userDto, User.class);
System.out.println(newUser);
assert user.getUsername().equals(newUser.getUsername());
assert user.getAge() == newUser.getAge();
assert user.isYoung() == newUser.isYoung();
}
}
比
MapStruct
的易用性高很多(需要自己编写的代码量更少了————弊端:但也增加了一重黑盒)
案例:单个类与单个类之间的映射(Car => CarDto)【必读】
@AutoMapper(target = CarDto.class)
public class Car {
// ...
}
- 该例子表示,会生成
Car
转换为CarDto
的接口CarToCarDtoMapper
及实现类CarToCarDtoMapperImpl
。
在自动生成的转换代码中,源类型(
Car
)的所有可读属性(即 不含private属性)将被复制到目标属性类型(CarDto
)的相应属性中。
-
当一个属性与它的目标实体对应物具有相同的名称时,将会被隐式映射。
-
除此之外,
MapStructPlus
会根据当前的默认规则,生成CarDto
转换为Car
的接口CarDtoToCarMapper
及实现类CarDtoToCarMapperImpl
。
如果不想生成该转换逻辑的话,可以通过注解的
reverseConvertGenerate
属性来配置。
案例:单个类与多个类之间的映射(UserDto、UserVo => User)
@Data
@AutoMappers({
@AutoMapper(target = UserDto.class),
@AutoMapper(target = UserVO.class)
})
public class User {
// fields
}
- 当想要配置一个类与多个类进行转换时,可以通过
@AutoMappers
来配置,该注解支持配置多个@AutoMapper
更多场景与案例
参见官方文档
K FAQ for MapStruct-Plus
Q: 解决io.github.linpeilie.ConvertException: cannot find converter from Car to CarDto
?
- 问题描述
- API 请求地址'/demo/car/list',发生未知异常.
io.github.linpeilie.ConvertException: cannot find converter from Car to CarDto
at io.github.linpeilie.Converter.convert(Converter.java:31)
at io.github.linpeilie.Converter.lambda$convert$0(Converter.java:54)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1625)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:682)
at io.github.linpeilie.Converter.convert(Converter.java:54)
at org.x.common.core.utils.MapstructUtils.convert(MapstructUtils.java:73)
at org.x.common.mybatis.core.mapper.BaseMapperPlus.selectVoList(BaseMapperPlus.java:174)
at java.base/java.lang.invoke.MethodHandle.invokeWithArguments(MethodHandle.java:732)
at com.baomidou.mybatisplus.core.override.MybatisMapperProxy$DefaultMethodInvoker.invoke(MybatisMapperProxy.java:166)
at com.baomidou.mybatisplus.core.override.MybatisMapperProxy.invoke(MybatisMapperProxy.java:89)
at jdk.proxy2/jdk.proxy2.$Proxy182.selectVoList(Unknown Source)
- 解决方法及效果
配置 MapStructPlus 的
@AutoMapper
注解
Y 推荐文献
- MapStruct-Plus
X 参考文献

本文链接: https://www.cnblogs.com/johnnyzen
关于博文:评论和私信会在第一时间回复,或直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
日常交流:大数据与软件开发-QQ交流群: 774386015 【入群二维码】参见左下角。您的支持、鼓励是博主技术写作的重要动力!