# 一.简单介绍
# 1.什么是 MapStruct?
MapStruct 是一个 Java 注解处理器框架,用于简化 Java Bean 之间的映射。它通过生成映射代码来消除手动编写映射代码的繁琐工作,从而提高开发效率。
# 2.MapStruct 的特点
- 简化映射: MapStruct 旨在简化 Java Bean 之间的映射,减少手动编写映射代码的工作量。
- 注解处理器: MapStruct 使用注解处理器技术,在编译时生成映射代码,而不是在运行时进行反射。
- 类型安全: 通过在映射接口上使用注解,MapStruct 能够在编译时检查映射的正确性,提供类型安全性。
- 支持自定义映射逻辑: 尽管 MapStruct 提供了默认的映射规则,但你仍然可以通过在映射接口中定义自定义方法来指定特定的映射逻辑。
# 二.基础使用
# 1.添加依赖
添加依赖: 首先,在你的项目中添加 MapStruct 的依赖。你可以在 Maven 或 Gradle 中进行配置。
Maven:
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.4.2.Final</version> <!-- 使用最新版本 -->
</dependency>
2
3
4
5
Gradle:
implementation 'org.mapstruct:mapstruct:1.4.2.Final' // 使用最新版本
annotationProcessor 'org.mapstruct:mapstruct-processor:1.4.2.Final'
2
# 2.定义映射接口
定义映射接口: 创建一个带有@Mapper
注解的接口,定义映射方法。
@Mapper
public interface CarMapper {
CarDto carToCarDto(Car car);
}
2
3
4
# 3.编写配置
编写映射配置: 如果需要自定义映射逻辑,可以在映射接口中定义自定义方法,或者使用@Mapping
注解。
@Mapper
public interface CarMapper {
@Mapping(source = "make", target = "manufacturer")
CarDto carToCarDto(Car car);
}
2
3
4
5
# 4.生成代码
生成映射代码: 编译项目时,MapStruct 注解处理器将生成映射代码。
# 5.调用方法
调用映射方法: 在应用程序中调用生成的映射方法。
CarMapper carMapper = Mappers.getMapper(CarMapper.class);
CarDto carDto = carMapper.carToCarDto(car);
2
以上是 MapStruct 的基本介绍和使用步骤。通过这些步骤,你可以轻松地实现 Java Bean 之间的映射,同时保持代码的简洁和易维护性。
# 6.一个 DTO 多个 DO
一个 DTO 对应多个 DO 的场景
@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface Converter extends FromConverter<StoreCategoryTypeOverviewDTO, AdsBaDayStoreOverviewRepDO> {
Converter INSTANCE = Mappers.getMapper(Converter.class);
}
@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface ConverterCategoryType extends FromConverter<StoreCategoryTypeOverviewDTO, AdsBaDayStoreCategoryTypeOverviewRepDO> {
ConverterCategoryType INSTANCE = Mappers.getMapper(ConverterCategoryType.class);
}
2
3
4
5
6
7
8
9
# 三.封装使用
# 1.FromConverter
public interface FromConverter<T, FROM> {
T from(FROM from);
List<T> from(List<FROM> list);
default PageBean<T> from(PageBean<FROM> page) {
PageBean<T> result = new PageBean<>();
result.setTotalElements(page.getTotalElements());
result.setNumber(page.getNumber());
result.setSize(page.getSize());
result.setTotalPages(page.getTotalPages());
result.setNumberOfElements(page.getNumberOfElements());
result.setContent(this.from(page.getContent()));
return result;
}
default PageBean<T> from(IPage<FROM> page) {
PageBean<T> result = new PageBean<>();
result.setTotalElements((int) page.getTotal());
result.setNumber((int) page.getCurrent());
result.setSize((int) page.getSize());
result.setTotalPages((int) page.getPages());
result.setNumberOfElements(page.getRecords().size());
result.setContent(this.from(page.getRecords()));
return result;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 2.PropertyCopier
public interface PropertyCopier<TARGET, T> {
void copy(@MappingTarget TARGET target, T t);
}
2
3
# 3.ToConverter
public interface ToConverter<T, TO> {
TO to(T t);
List<TO> to(List<T> list);
default PageBean<TO> to(PageBean<T> page) {
PageBean<TO> result = new PageBean<>();
result.setTotalElements(page.getTotalElements());
result.setNumber(page.getNumber());
result.setSize(page.getSize());
result.setTotalPages(page.getTotalPages());
result.setNumberOfElements(page.getNumberOfElements());
result.setContent(this.to(page.getContent()));
return result;
}
default PageBean<TO> to(IPage<T> page) {
PageBean<TO> result = new PageBean<>();
result.setTotalElements((int) page.getTotal());
result.setNumber((int) page.getCurrent());
result.setSize((int) page.getSize());
result.setTotalPages((int) page.getPages());
result.setNumberOfElements(page.getRecords().size());
result.setContent(this.to(page.getRecords()));
return result;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 4.TwoWayConverter
public interface TwoWayConverter<SRC, DEST> extends ToConverter<SRC, DEST>, FromConverter<SRC, DEST> {
}
2
# 5.基本使用
@Data
public class ChatbotDTO {
private Integer id;
private String question;
private String response;
private Date createTime;
private Integer isDelete;
@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface Converter extends FromConverter<ChatbotDTO, Chatbot> {
Converter INSTANCE = Mappers.getMapper(Converter.class);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
@Data
public class Chatbot extends Model<Chatbot> {
private Integer id;
private String question;
private String response;
private Date createTime;
private Integer isDelete;
}
2
3
4
5
6
7
8
# 6.原理分析
打开 target 目录,可以看到如图所示的编译后生成的类,说明在编译器就生成了对应的 DTO,相比其他 2 种方式在运行时生成 DTO 更加高效。
# 7.使用技巧
- 忽略字段映射
- 源对象与目标对象属性名不符的映射方法
- 直接设置特定值
- 表达式映射
- 自定义映射
# 8.本地启动失败
-Djps.track.ap.dependencies=false
# 四.工具对比
# 1.概述
MapStruct、Apache BeanUtils 以及 Spring BeanUtils 都是用于 Java Bean 之间属性复制的工具,但它们有一些区别,包括实现方式、性能、功能等方面。
# 2.MapStruct
优势:
- 编译时检查: MapStruct 在编译时生成映射代码,提供了类型安全性,可以在编译时捕获一些映射错误。
- 性能: 由于生成的映射代码是直接的 Java 代码,MapStruct 通常在性能上优于运行时反射的解决方案。
- 可配置性: 提供了很高的自定义能力,你可以在映射接口中定义自定义方法或使用
@Mapping
注解来指定映射规则。 - MapStruct 能自动的将名字一样,类型不一致的属性值进行转化再赋值(仅限基本类型的包装类型,如果转化值适配不了对应类型会报错)
不足:
- 学习曲线: 对于初学者而言,可能需要一些时间来理解和配置 MapStruct。
# 3.Apache BeanUtils
优势:
- 简单易用: Apache BeanUtils 提供了简单的 API,易于上手。
- 反射: 使用反射机制实现属性复制,可以在运行时处理动态对象。
不足:
- 性能: 相对于 MapStruct,Apache BeanUtils 在性能上可能较慢,因为它使用反射进行属性复制。
- 类型安全: 由于是在运行时进行反射,缺乏编译时类型检查。
# 4.Spring BeanUtils
优势:
- 集成 Spring: Spring BeanUtils 是 Spring 框架的一部分,可以很容易地集成到 Spring 应用程序中。
- 简化代码: 提供了一些方便的方法,可以简化属性复制的代码。
不足:
- 性能: 与 Apache BeanUtils 类似,Spring BeanUtils 也是使用反射,因此性能相对较低。
- 可配置性: 相较于 MapStruct,可配置性相对较低。
# 5.总结:
MapStruct 提供了编译时类型检查和高度可配置性,适用于需要更高性能和更复杂映射规则的场景。
Apache BeanUtils 和 Spring BeanUtils 简单易用,适用于简单的属性复制场景,但在性能和类型安全性方面可能相对较差。
选择使用哪个工具取决于你的具体需求,如果性能和类型安全性是关键考虑因素,而且你愿意付出一些学习成本,那么 MapStruct 可能是一个更好的选择。如果你需要一个简单且易于使用的解决方案,并且性能要求不是很高,那么 Apache BeanUtils 或 Spring BeanUtils 可能是更合适的选择。
← 03-PlantUML 05-驼峰下滑互转 →