领域架构对象转换利器MapStruct

领域架构对象转换利器MapStruct

文章图片

领域架构对象转换利器MapStruct

做java开发的程序员相信都读过阿里版的开发规范 , 可能还不只读一次 , 是反反复复的读那种 , 在阿里开发规范有这么一节应用分层 。 从显示层->web请求处理层->service业务逻辑层->manger通用业务处理层->dao数据持久层 , 上层依赖下层这么一个工程结构 , 这也是目前大多数企业开发使用的工程分层 , 每个公司大同小异 。

同时为了层与层之间的解耦 , 也会有对应的分层领域模型 , 前端到web层用VO对象 , 显示层到Service层用DTO对象 , Service层到dao数据层用DO对象 , 虽然这种领域分层的思想能带来层与层之间的逻辑解耦 , 让每层专注于自己的核心职责和逻辑 , 避免所有代码写在一起像一团乱麻一样 , 提升了代码的可读性和可维护性 , 但同样也带来另一个问题 , 层与层之间为了解耦 , 定义了太多的领域对象模型 , 对象模型之间需要进行转换 , 开发的成本直线上升 , 那么有没有办法能减少这块的工作呢?答案是肯定的 。
对象转换的几种方法【领域架构对象转换利器MapStruct】一、get/set赋值方式:这种一般用于对象属性较少如果碰到对象属性多的情况 , 就很容易眼花缭乱 , 一不小心就写错了 , 耗时耗力先不说 , 同时也会带来整个代码段非常的臃肿几十上百行都是在get/set 。
二、Apache BeanUtils:直接用apache的工具类BeanUtils.copyProperties进行对象拷贝 , 但使用了很多反射 , 做了很多校验 , 所以导致性能较差 , 一般用在对性能要求不太高的业务中去使用 , 如果你是使用了spring 框架 , 也可以使用Spring 自带的BeanUtils.copyProperties , 在性能方面比它要好些 , 去掉了一些不必要的校验以及增加了一些缓存的机制 , 加快了转换的速度 。
三、MapStruct:它是基于编译时的一个框架 , 在编译期间就将代码进行生成class文件 , 所以和我们使用get/set 方法的性能是一致的 。
当然还有一些不是很常用的转换方法 , 比如通过JSON的方式进行转换 , 通过基于cglib的动态代理BeanCopier的方式进行转换等等 。
MapStruct
一、如何使用?
拿官方的使用文档的说明作为例子
第一步:引入jar包 , 目前最新版本是1.6.2版本 , 因为是编译时生成class文件 , 所以还需要引入maven plugin
...<properties><org.mapstruct.version>1.6.2</org.mapstruct.version></properties>...<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>...第二步:准备好两个需要进行转换的对象
public class Car {private String make;private int numberOfSeats;private CarType type;//constructor getters setters etc.public class CarDto {private String make;private int seatCount;private String type;//constructor getters setters etc.第三步:定义转移的mapper类 , 如果原对象属性和目标对象属性不一致 , 可以使用mapping进行字段映射 , 一致的话就不需要进行映射了 。
@Mapper public interface CarMapper {CarMapper INSTANCE = Mappers.getMapper( CarMapper.class );@Mapping(source = \"numberOfSeats\" target = \"seatCount\")CarDto carToCarDto(Car car); 一个简单的对象之间的转换就完成了 , 是不是so easy 。
当然针对于一些复杂的属性转换 , 也可以自己自定义转换类 , 详细可以参考mapstrut的官方使用文档https://mapstruct.org/documentation/stable/reference/html
二、MapStruct避坑误区
对象属性转换的坑
1、MapStruct能够帮助我们在一些类型之间进行自动转换 , 比如Long 映射成为Integer , 这个时候就会出现当Long的数值超过Integer的最大值 , 出现值溢出的现象
2、对于一些日期的转换 , Mapstruct进行时间格式化时 , 采用的是默认时区UTC , 这就会导致时间出现问题 。
要避免此类问题 , 尽量避免出现原对象的属性类型与目标对象的属性一致 , 实在要进行转换 , 需要查看MapStruct生成后的源码情况时什么 。
.....
和Lombok共用的坑
如果我们对象使用 Lombok 的话 , 使用 @Mapping指定不同字段名 , 编译期间可能会报错 , 这是因为Lombok 也是编译期时自动生成代码 , 这两者可能导致冲突 , 当 MapStruct 生成代码时 , 还不存在 Lombok 生成的代码 。
解决这类问题的方法 , 就是maven plugin增加lombok 。
<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><path><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>${org.projectlombok.version</version></path></annotationProcessorPaths></configuration></plugin></plugins></build>当然mapstrut使用还有很多其他的误区 , 要避免在使用过程尽量不犯错或者是少犯错 , 建议多看下它编译后生成的源码文件 , 毕竟自动生成的代码也有可能存在问题 , 通过查看这些代码 , 也可以帮助我们了解mapstrut是如何映射的 , 有助于我们发现问题解决问题 。

    推荐阅读