顶级工具,性能爆棚的 Java 实体转换 / 复制神器
来源:blog.csdn.net/qq_30231473/article/details/129782881
👉 欢迎加入小哈的星球 ,你将获得: 专属的项目实战/ Java 学习路线 / 一对一提问 / 学习打卡/赠书福利
目前, 正在星球内部带小伙伴做第一个项目:全栈前后端分离博客,手摸手,后端 + 前端全栈开发,从 0 到 1 讲解每个功能点开发步骤,1v1 答疑,直到项目上线。目前已更新了125小节,累计18w+字,讲解图:805张,还在持续爆肝中.. 后续还会上新更多项目,目标是将Java领域典型的项目都整一波,如秒杀系统, 在线商城, IM即时通讯,Spring Cloud Alibaba 等等,戳我加入学习,已有400+小伙伴加入(早鸟价超低)
-
1 优点 -
2 性能对比 -
3 使用 -
依赖 -
定义转换接口 -
编译结果 -
调用 -
4 插件 -
特性 -
5 其他用法 -
基础映射 -
映射器添加自定义方法 -
多个源参数映射 -
嵌套属性映射到当前目标 -
表达式方式 -
更新现有实例 -
Map映射 -
更多用法 -
6 总结
Java项目中实体转换无处不在,当实体字段较多或者大批量的进行复制时,通过手工setter/getter显得太LOW,同时兼备高性能要求情况下,MapStruct完全完全能够胜任。
官方解释,MapStruct是一个代码生成器,它基于约定优于配置的方法,极大地简化了Java bean类型之间映射的实现。生成的映射代码使用普通方法调用,因此快速、类型安全且易于理解。因为MapStruct是在编译期间生成setter/getter方法,实际运行时就是直接调用setter/getter,效率会非常高。
1 优点
-
MapStruct编译期生成映射代码,所以可以在编译时暴露映射错误的代码,让错误提前暴露; -
因为使用setter/getter方式,而非反射方式,所以可以更快的执行效率; -
可以实现深拷贝,自动类型转换,如枚举转换; -
进行自定义的映射,多种映射方式,下边具体说明;
2 性能对比
对比对象 | 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 |
3 使用
依赖
<!-- MapStruct核心,包含了一些必要的注解-->
<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>
<!-- MapStruct编译,注解处理器,根据注解自动生成Mapper的实现 -->
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
定义转换接口
/**
* 测试接口
*
* @author reboot
*/
@Mapper
public interface OrderConvertor {
/**
* 实例
*/
OrderConvertor INSTANCE = Mappers.getMapper(OrderConvertor.class);
/**
* OrderDo -> OrderModel
*
* @param orderDo 订单实体
* @return {@link OrderModel}
*/
OrderModel toModel(OrderDo orderDo);
/**
* OrderDo -> OrderModel
*
* @param orderDos 订单实体
* @return {@link OrderModel}
*/
List<OrderModel> toModel(List<OrderDo> orderDos);
/**
* OrderModel -> OrderDo
*
* @param orderModel 订单模型
* @return {@link OrderDo}
*/
OrderDo toDo(OrderModel orderModel);
/**
* OrderModel -> OrderDo
*
* @param orderModels 订单模型
* @return {@link OrderDo}
*/
List<OrderDo> toDo(List<OrderModel> orderModels);
}
编译结果
MapStruct会自动生成对应接口的实现,并自动完成属性映射关系,List会自动进行批量处理。
调用
/**
* 订单服务
*
* @author reboot
*/
@Service
public class OrderService {
/**
* 获取订单列表
*
* @return {@link List}<{@link OrderModel}>
*/
public List<OrderModel> getOrderList() {
// 获取数据库数据DO
List<OrderDo> result = selectOrderList();
// 参数转换
return OrderConvertor.INSTANCE.toModel(result);
}
}
4 插件
上边的使用方式虽然能够正常使用,但是在一些属性配置映射上和提示上,如果使用插件能够提升使用体验,IDEA中可以直接安装Mapstruct Support插件,当然Eclipse也有对应的插件。
特性
-
突出显示目标属性和源属性。将目标属性和源属性转到声明的setter / getter中;
-
错误和快速修复:
-
-
缺少@Mapper或@MapperConfig注解检查; -
快速修复未映射的目标属性,添加未映射目标属性和忽略未映射目标属性;
5 其他用法
更加详细的内容可以查看官方文档,发布文章时最新版本是 MapStruct 1.5.3.Final.html。
基础映射
@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表示源属性名,一般在目标属性和源属性不同时使用,相同的属性名会自动进行映射。
映射器添加自定义方法
@Mapper
public interface CarMapper {
@Mapping(...)
...
CarDto carToCarDto(Car car);
default PersonDto personToPersonDto(Person person) {
//hand-written mapping logic
}
}
自定义方法personToPersonDto并实现,在生成的实现类中会进行覆盖使用。
多个源参数映射
@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);
}
存在多个源参数,使用参数名.属性名的方式进行表示,也可以直接使用基础类型的属性名称。
嵌套属性映射到当前目标
@Mapper
public interface CustomerMapper {
@Mapping( target = "name", source = "record.name" )
@Mapping( target = ".", source = "record" )
@Mapping( target = ".", source = "account" )
Customer customerDtoToCustomer(CustomerDto customerDto);
}
当源参数中存在对象属性,可以手动进行映射,或者直接使用"."的方式将对象中的属性全部映射到当前目标对象。
表达式方式
@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代码块进行转换,一般可以将静态方法处理的字段放到这里。
更新现有实例
@Mapper
public interface CarMapper {
void updateCarFromDto(CarDto carDto, @MappingTarget Car car);
}
@MappingTarget源参数,编译时会将carDto参数中的属性映射到car参数中。
Map映射
@Mapper
public interface CustomerMapper {
@Mapping(target = "name", source = "customerName")
Customer toCustomer(Map<String, String> map);
}
直接将map中的key进行映射。
更多用法
还有更多其他用法,比如:
-
支持映射定义的public属性; -
支持映射参数Builder模式; -
使用注入方式引入转换器; -
数据类型字段转换,如枚举、日期,支持日期格式化,支持数字类型格式化 -
集合类型自动转换; -
转换Stream;
6 总结
MapStruct还有很多其他高阶特性,限于篇幅文章仅仅列举部分示例,有兴趣的同学可以查看对应文档试试。使用适当的工具有效提高编程效率,在使用工具过程中我们也了解其实现原理,不断提高自身。
👉 欢迎加入小哈的星球 ,你将获得: 专属的项目实战/ Java 学习路线 / 一对一提问 / 学习打卡/赠书福利
目前, 正在星球内部带小伙伴做第一个项目:全栈前后端分离博客,手摸手,后端 + 前端全栈开发,从 0 到 1 讲解每个功能点开发步骤,1v1 答疑,直到项目上线。目前已更新了125小节,累计18w+字,讲解图:805张,还在持续爆肝中.. 后续还会上新更多项目,目标是将Java领域典型的项目都整一波,如秒杀系统, 在线商城, IM即时通讯,Spring Cloud Alibaba 等等,戳我加入学习,已有400+小伙伴加入(早鸟价超低)
1. 我的私密学习小圈子~
最近面试BAT,整理一份面试资料《Java面试BATJ通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。
获取方式:点“在看”,关注公众号并回复 Java 领取,更多内容陆续奉上。
PS:因公众号平台更改了推送规则,如果不想错过内容,记得读完点一下“在看”,加个“星标”,这样每次新文章推送才会第一时间出现在你的订阅列表里。
点“在看”支持小哈呀,谢谢啦