前言
MapStruct
是一个Java 注解处理器 ,用于生成类型安全的bean
映射类。
我们需要做的就是定义一个映射接口,声明映射方法。在编译期间,MapStruct
将生成此接口的实现类。此实现使用简单的 Java 方法调用(getter
setter
…)在源对象和目标对象之间进行属性映射,没有使用反射或类似的内容。
与手动编写属性映射代码相比,MapStruct
通过生成冗长且易于出错的代码来节省时间。按照约定优于配置,MapStruct
使用合理的默认值(用户没有自定义配置或实现特殊行为)。
与动态映射框架相比,MapStruct
具有以下优点:
- 通过使用普通方法调用而不是反射来快速执行
- 编译时类型安全性:只能映射彼此映射的对象和属性(不能将订单实体意外映射到客户DTO等)。
- 编译期错误报告:
- 映射不完整(并非所有目标属性都被映射)
- 映射不正确(找不到正确的映射方法或类型转换)
引入依赖
项目使用 Maven
构建,其他方式如 Gradle
,Ant
等请查考官网 设置
1 | ... |
基本使用
基本映射
要创建映射器,只需定义一个 Java
接口,并使用 org.mapstruct.Mapper
注解:
1 | @Mapper |
该 @Mapper
注解将使用 MapStruct
的代码生成器创建 StudentMapper
接口的实现 StudentMapperImpl
,在生成的方法实现中,源类型(例如Staff
)的所有可读属性都将被复制到目标类型(例如Student
)的相应属性中:
- 当一个属性与其目标实体对应的名称相同时,它将被隐式映射。
- 当属性在目标实体中具有不同的名称时,可以通过
@Mapping
注解指定其名称。
StudentMapperImpl.class
反编译如下:
1 | public class StudentMapperImpl implements StudentMapper { |
单元测试如下:
1 | @Test |
多源参数映射
多源参数映射即多对一映射,使用 @Mapping(source = "student.studentName", target = "name")
可以将 student
的 studentName
属性 映射(复制)到 Person
的 name
上,代码如下:
1 | @Mapper |
更新现有Bean
在某些情况下,不需要创建目标类型的新实例,而是更新该类型的现有实例。可以通过为目标对象添加一个参数并将其标记为 @MappingTarget
, 其中,person1的同名属性会完全覆盖(更新)person2的同名属性,如下
1 |
|
使用依赖注入
这里使用 Spring 容器来介绍 MapStruct
如何使用依赖注入,我们需要通过 Mapper#componentModel
来指定DI
类型,目前支持
default:映射器不使用组件模型,实例通常通过
Mappers.getmapper
(类)获取。cdi:生成的映射器是应用程序范围的
cdi bean
,可以通过@Inject
获取。spring:生成的映射器是一个
spring bean
,可以通过@Autowired
获取。jsr330:生成的映射器用`@javax.inject.named
和
@singleton注释,可以通过
@inject` 获取。
实例代码如下:
1 | @Mapper(componentModel = "spring") |
调用其他映射器
虽然我们可以通过 @Mapping
注解来灵活的映射不同实体的属性对应关系,但是遇到日期类型格式化为字符串类型,我们不得不复制粘贴例如 @Mapping(target = "registryDate", dateFormat = "dd.MM.yyyy")
这样的映射关系,有没有什么更为简单通用的实现呢,MapStruct
提供了 Mapper#uses
,我们可以自定义一些高级映射,例如 DateMapper
,代码如下:
1 | @Component |
这样就能将 Date registryDate
通过自定义的转换方式转化为 String registryDate
。
补充说明
这里补充一些 Mapping
的其他用法:
- 忽略映射字段:
@Mapping(target = "gender", ignore = true)
- 日期格式化:
@Mapping(target = "registryDate", dateFormat = "dd.MM.yyyy")
- 数值格式化:
@Mapping(source = "price", numberFormat = "$#.00")
- 表达式映射:
@Mapping(target = "timeAndFormat", expression = "java(new org.sample.TimeAndFormat( s.getTime(), s.getFormat())
- 默认表达式:
@Mapping(target="id", source="sourceId", defaultExpression = "java( UUID.randomUUID().toString() )")
- …
其他用法,如有兴趣,可移步 官方文档 。