Mapstruct
# What is MapStruct
MapStruct 可以通过注解配置的方法完成 JavaBean 之间的属性对拷。 其通过配置的方式统一管理 JavaBean 之间的拷贝关系, 虽然相对比 BeanUtils 这样的工具需要多一层配置,但是在大型项目中,遍布在各个角落的 BeanUtils 遇到修改的需求时,MapStruct 的优势就很明显了。
另外, MapStruct 还支持更加灵活的属性拷贝方案,其性能也比 BeanUtils 要好很多。
# Why MapStruct
用为什么需要属性对拷
来回答为什么需要 MapStruct 是个不错的方式。 在数据传输的过程中,用户往往不需要访问记录的所有属性。所以当从数据库中查询出一条记录时,需要将部分需要的属性重新封装成一个 JavaBean。 DTO(Data Transfer Object)就是一个例子。 此时往往需要大量的代码来执行 Model 到 Dto 的转换。
当需要用一个 JavaBean 的属性值来初始化另外一个 JavaBean 时,Mapstruct 通过一次配置,即可快速的完成 JavaBean 转换。从而避免了大量的 A.setA(B.getA());
代码。
# How to use Mapstruct
# Import
<!-- 在 pom.xml 中配置 maven依赖 -->
<dependencies>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.4.2.Final</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.4.2.Final</version>
</dependency>
</dependencies>
2
3
4
5
6
7
8
9
10
11
12
13
# 基本映射
/**
* 模型类 Person
*/
class Person {
private Integer id;
private String name;
}
/**
* Dto
*/
class PersonDto {
private Integer id;
private String name;
}
/**
* 映射器
*/
@org.mapstruct.Mapper
public interface PersonMapper {
PersonMapper INSTANCE = Mappers.getMapper(PersonMapper.class);
PersonDto toDto(Person person);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
上面代码中三个类分别定义了 Person,PersonDto, PersonMapper。 当我们拥有 Person 实例的时候,可以通过下面的代码迅速获得 PersonDto 实例。
PersonDto personDto = PersonMapper.INSTANCE.toDto(person);
# 自定义映射关系
可以在方法上添加注解 @Mapping
来自定义映射关系。
@Mapper
public interface PersonMapper {
PersonMapper INSTANCE = Mappers.getMapper(PersonMapper);
@Mapping(source = "person.name", target = "id")
PersonDto toDto(Person person);
}
2
3
4
5
6
7
# 跳过空值覆盖
@Mapper
public interface PersonMapper {
@BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
void toDomain(Person.Update update, @MappingTarget Person person);
}
2
3
4
5
# 多个输入源
MapStruct 支持将多个实例的属性值汇聚到一个实例内。
@Mapper
public interface PersonMapper {
Personmapper INSTANCE = Mappers.getMapper(PersonMapper.class);
@Mapping(source = "person.name", target = "name")
@Mapping(source = "school.name", target = "school")
PersonDto toDto(Person person, School school);
}
2
3
4
5
6
7
8
# 自动转换成员属性
当我们有 A, ADto, B, BDto 四个类时。其中B类是这样的:
class B {
List<A> aList;
}
2
3
如果我们期望的 BDto 内部是这样的:
class BDto {
List<ADto> aList;
}
2
3
首先,我们要定义 A -> ADto
的映射关系:
@Mapper
public interface AMapper{
AMapper m = Mappers.getMapper(AMapper.class);
ADto toDto(A);
}
2
3
4
5
6
然后在定义 B -> BDto
时通过uses指令
指定使用 AMapper 来处理成员变量的转换。
@Mapper(uses = {AMapper.class})
public interface BMapper {
BMapper m = Mappers.getMapper(BMapper.class);
BDto toDto(B);
}
// 可以直接通过下面获得含有 List<ADto> 的 BDTo 实例。
BDto BDto = BMapper.INSTANCE.toDto(B);
2
3
4
5
6
7
8
9
# 依赖注入
直接将 Mapper 放入 Spring IOC 容器中。
@Mapper(componentModel = "spring")
public interface PersonMapper{}
// 相当于
@Component
public class PersonMapperImpl implements PersonMapper{}
2
3
4
5
6
可以通过@Autowired
来自动注入。
@RestController
class HelloControlelr {
@Autowired
private PersonMapper personMapper;
}
2
3
4
5
# 自定义映射方法
在 Mapper 接口中,使用 default 修饰方法名后,可以定义方法体,在方法体中定义转换的方法。
@Mapper
public interface Person {
default PersonDto toDto(Person person) {
retrun PersonDto.builder().id(person.getId()).name(person.getName()).build();
}
}
2
3
4
5
6
也可以通过编写抽象类的方法来自定义转换方法。
@Mapper
public abstract class PersonMapper {
public PersonDto toDto(Person person) {
return PersonDto.builder().id(person.getId()).name(person.getName()).build();
}
}
2
3
4
5
6