![image.png](https://cdn.nlark.com/yuque/0/2023/png/26851474/1679816286562-e6089362-0645-482a-91c9-6e371b40d3ca.png#averageHue=%23e7e2e1&clientId=u4427093c-483e-4&from=paste&height=235&id=u5b423a6c&originHeight=470&originWidth=1452&originalType=binary&ratio=2&rotation=0&showTitle=false&size=59073&status=done&style=none&taskId=u6559635e-cc24-433d-ac56-6a4e1231a99&title=&width=726)
Java项目中实体转换
无处不在,当实体字段较多或者大批量的进行复制时,通过手工setter/getter显得太LOW,同时兼备高性能要求情况下,MapStruct
完全完全能够胜任。官方解释,MapStruct是一个代码生成器,它基于约定优于配置的方法,极大地简化了Java bean类型之间映射的实现。生成的映射代码使用普通方法调用,因此快速、类型安全且易于理解。因为MapStruct是在编译期间生成setter/getter方法,实际运行时就是直接调用setter/getter,效率会非常高。
优点
- MapStruct编译期生成映射代码,所以可以在编译时暴露映射错误的代码,让
错误提前暴露
;
- 因为使用
setter/getter
方式,而非反射方式,所以可以更快的执行效率;
- 可以实现
深拷贝
,自动类型转换,如枚举转换;
- 进行自定义的映射,
多种映射方式
,下边具体说明;
性能对比
对比对象 |
10个对象复制1次 |
1万个对象复制1次 |
100万个对象复制1次 |
100万个对象复制5次 |
MapStruct |
0ms |
3ms |
96ms |
281ms |
Hutools的BeanUtil |
23ms |
102ms |
1734ms |
8316ms |
Spring的BeanUtils |
2ms |
47ms |
726ms |
3676ms |
Apache的BeanUtils |
20ms |
156ms |
10658ms |
52355ms |
Apache的PropertyUtils |
5ms |
68ms |
6767ms |
30694ms |
使用
依赖
1 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 29
| <dependencies> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct</artifactId> <version>${org.mapstruct.version}</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <source>1.8</source> <target>1.8</target> <annotationProcessorPaths> <path> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>${org.mapstruct.version}</version> </path> </annotationProcessorPaths> </configuration> </plugin> </plugins> </build>
|
定义转换接口
1 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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
|
@Mapper public interface OrderConvertor {
OrderConvertor INSTANCE = Mappers.getMapper(OrderConvertor.class);
OrderModel toModel(OrderDo orderDo);
List<OrderModel> toModel(List<OrderDo> orderDos);
OrderDo toDo(OrderModel orderModel);
List<OrderDo> toDo(List<OrderModel> orderModels); }
|
编译结果
![image.png](https://cdn.nlark.com/yuque/0/2023/png/26851474/1679819568669-d8de93c0-7c36-468c-b273-27956d94c768.png#averageHue=%23282521&clientId=u4427093c-483e-4&from=paste&height=379&id=ua264b2bb&originHeight=758&originWidth=2394&originalType=binary&ratio=2&rotation=0&showTitle=false&size=221446&status=done&style=none&taskId=u851c2f4c-3294-4145-afe4-56b0a98574b&title=&width=1197)
MapStruct会自动生成对应接口的实现,并自动完成属性映射关系,List会自动进行批量处理。
调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
@Service public class OrderService {
public List<OrderModel> getOrderList() { List<OrderDo> result = selectOrderList(); return OrderConvertor.INSTANCE.toModel(result); } }
|
插件
![image.png](https://cdn.nlark.com/yuque/0/2023/png/26851474/1679824343665-2229027c-be3c-4d0c-835a-95d7a35329c9.png#averageHue=%231d1c1c&clientId=u4427093c-483e-4&from=paste&height=64&id=yluYK&originHeight=128&originWidth=1372&originalType=binary&ratio=2&rotation=0&showTitle=false&size=16035&status=done&style=none&taskId=ufe0e0871-ee32-4894-8121-71310df25b9&title=&width=686)
上边的使用方式虽然能够正常使用,但是在一些属性配置映射上和提示上,如果使用插件能够提升使用体验,IDEA中可以直接安装Mapstruct Support插件,当然Eclipse也有对应的插件。
特性
- 突出显示目标属性和源属性。将目标属性和源属性转到声明的setter / getter中;
- 错误和快速修复:
- 缺少@Mapper或@MapperConfig注解检查;
- 快速修复未映射的目标属性,添加未映射目标属性和忽略未映射目标属性;
![image.png](https://cdn.nlark.com/yuque/0/2023/png/26851474/1679825260995-406e3ecb-0687-4cd9-b9e3-f11f4c2e6b1e.png#averageHue=%231a1918&clientId=u4427093c-483e-4&from=paste&height=311&id=SYRsp&originHeight=622&originWidth=1488&originalType=binary&ratio=2&rotation=0&showTitle=false&size=147075&status=done&style=none&taskId=ud81bb013-61eb-4529-8473-90b6d1a4ea5&title=&width=744)
其他用法
更加详细的内容可以查看官方文档,发布文章时最新版本是 MapStruct 1.5.3.Final.html。
基础映射
1 2 3 4 5 6 7 8 9 10
| @Mapper public interface CarMapper {
@Mapping(target = "manufacturer", source = "make") @Mapping(target = "seatCount", source = "numberOfSeats") CarDto carToCarDto(Car car);
@Mapping(target = "fullName", source = "name") PersonDto personToPersonDto(Person person); }
|
target
表示目标属性名,source
表示源属性名,一般在目标属性和源属性不同时使用,相同的属性名会自动进行映射。
映射器添加自定义方法
1 2 3 4 5 6 7 8 9 10 11
| @Mapper public interface CarMapper {
@Mapping(...) ... CarDto carToCarDto(Car car);
default PersonDto personToPersonDto(Person person) { } }
|
自定义方法personToPersonDto并实现,在生成的实现类中会进行覆盖
使用。
多个源参数映射
1 2 3 4 5 6 7 8 9 10 11
| @Mapper public interface AddressMapper {
@Mapping(target = "description", source = "person.description") @Mapping(target = "houseNumber", source = "address.houseNo") DeliveryAddressDto personAndAddressToDeliveryAddressDto(Person person, Address address);
@Mapping(target = "description", source = "person.description") @Mapping(target = "houseNumber", source = "hn") DeliveryAddressDto personAndAddressToDeliveryAddressDto(Person person, Integer hn); }
|
存在多个源参数,使用参数名.属性名
的方式进行表示,也可以直接使用基础类型
的属性名称。
嵌套属性映射到当前目标
1 2 3 4 5 6 7 8
| @Mapper public interface CustomerMapper {
@Mapping( target = "name", source = "record.name" ) @Mapping( target = ".", source = "record" ) @Mapping( target = ".", source = "account" ) Customer customerDtoToCustomer(CustomerDto customerDto); }
|
当源参数中存在对象属性,可以手动进行映射,或者直接使用”.”的方式将对象中的属性全部映射到当前目标对象。
表达式方式
1 2 3 4 5 6 7 8 9 10 11
| @Mapper public interface SourceTargetMapper {
SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class );
@Mapping( target = "timeAndFormat", expression = "java( new org.sample.TimeAndFormat( s.getTime(), s.getFormat() ) )" ) Target sourceToTarget(Source s); }
|
支持使用java代码块进行转换,一般可以将静态方法处理的字段放到这里。
更新现有实例
1 2 3 4 5
| @Mapper public interface CarMapper {
void updateCarFromDto(CarDto carDto, @MappingTarget Car car); }
|
@MappingTarget
源参数,编译时会将carDto参数中的属性映射到car参数中。
Map映射
1 2 3 4 5 6
| @Mapper public interface CustomerMapper {
@Mapping(target = "name", source = "customerName") Customer toCustomer(Map<String, String> map); }
|
直接将map
中的key进行映射。
更多用法
还有更多其他用法,比如:
- 支持映射定义的public属性;
- 支持映射参数Builder模式;
- 使用注入方式引入转换器;
- 数据类型字段转换,如枚举、日期,支持日期格式化,支持数字类型格式化,具体可以看 Implicit type conversions;
- 集合类型自动转换;
- 转换Stream;
- ……
总结
MapStruct还有很多其他高阶特性,限于篇幅文章仅仅列举部分示例,有兴趣的同学可以查看对应文档试试。使用适当的工具有效提高编程效率,在使用工具过程中我们也了解其实现原理,不断提高自身。后边有时间也把MapStruct实现原理拿出来讲讲,跟大家一起学习进步!