前言
本文将会主要讲解后端开发中 VO、DTO、Entity 相互转化方式,并且针对其中比较成熟的框架 MapStruct 进行解读和教学

问题
微服务架构下,服务拆分会产生 VO、DTO、Entity 三类 POJO
-
VO 用于前端接口参数传递,例如用于 http 接口接收请求参数。可以继承扩展 DTO,或者直接使用 DTO -
DTO 用于 rpc 接口参数传递。单独定义,或者继承扩展其他 rpc 接口的 DTO -
Entity(PO) 用于 orm 映射处理,与表结构对应,只在服务内部使用,不能对外
注:对于 POJO 的解释可以查看文章后面的 补充 章节
微服务架构面向不同场景的 POJO 定义,引入前端请求处理问题,也就是三者之间的转换:
-
请求:VO => DTO => Entity -
返回:Entity => DTO => VO
结构
Entity
@Data
@TableName(value = "orders", schema = "crazy1984")
public class Order {
@TableId(type = IdType.AUTO)
private int id;
private String orderId;
private String orderType;
private int orderStatus;
private Date createdAt;
private Date updatedAt;
}
DTO
@Data
public class OrderDTO {
private String orderId;
private String orderType;
private int orderStatus;
private Date createdAt;
}
VO
@Data
public class OrderVO extends OrderDTO{
private String orderTypeName;
private String orderStatusName;
}
手动转换
我们可以使用最直接的方法,通过代码对 POJO 属性进行逐个拷贝
但是这样的方式太低效,给开发人员增加许多低效的重复劳动,也不易维护(比如新增字段时,所有相关处都要同步修改)
OrderDTO dto = new OrderDTO();
dto.setOrderId(entity.getOrderId());
dto.setOrderType(entity.getOrderType());
dto.setOrderStatus(entity.getOrderStatus());
dto.setCreatedAt(entity.getCreatedAt());
工具类转换
改进方法为,使用工具类进行同名属性的自动拷贝,例如使用 Spring 的 BeanUtils
OrderDTO dto = new OrderDTO();
BeanUtils.copyProperties(entity, dto);
这样可以减少大量工作,但是会带来如下不足:
-
不支持属性名映射,属性名必须完全相同 -
不支持自动类型转换,Spring 的 BeanUtils 要求源属性与目标属性的类型是相互 assignable 的 -
性能损耗大,属性拷贝中的属性名匹配、类型检查、写权限检查都是动态判断,有性能损耗
除此之外,还有很多工具类,下图为各类工具类对比:
拷贝工具 | 使用效率 |
---|---|
Spring BeanUtils | 使用方便,效率中等 |
Cglib BeanCopier | 使用方便,效率最高 |
Apache BeanUtils | 使用方便,效率低,原因为该工具做了很多校验,兼容,日志打印等,导致性能下降(阿里约束规范中禁止使用该工具) |
Apache PropertyUtils | 使用方便,效率低 |
Hutool BeanUtil | 使用方便,封装完善,效率较高 |
映射框架转换
目前有很多开源成熟的 mapping 框架:
-
Dozer – Usage (sourceforge.net)[1] -
Orika reference guide (orika-mapper.github.io)[2] -
ModelMapper – Simple, Intelligent, Object Mapping.[3] -
MapStruct – Java bean mappings, the easy way![4] -
JMapper Framework (jmapper-framework.github.io)[5]
以上框架均支持不同属性名的映射,自动类型转换,递归映射自定义对象属性
实现原理为基于反射机制,实现类属性的 get,set 调用,基于注解、配置,来实现不同属性名的映射和类型转化
框架的性能对比,MapStruct
和 JMapper
性能较好。因为他们的映射过程是静态化的,所以实际性能和自己手写 get、set 一样
并且在开发过程中,可以通过检查生成的代码来确保映射转换没有错误,相比 ModelMapper 的黑盒实现更加可靠。对于 grpc 协议 protobuf 对象和 entity 的互相转换,也能很好的支持
下面我们将会详细讲解 MapStruct 框架的使用
MapStruct
简介
官方仓库:mapstruct/mapstruct: An annotation processor for generating type-safe bean mappers (github.com)[6]
官方文档:MapStruct – Java bean mappings, the easy way![7]
MapStruct 是代码生成器,基于约定而不是配置,极大地简化 Java Bean 类型之间映射的实现
安装
官方文档:Installation – MapStruct[8]
首先我们需要向 pom.xml
中添加如下内容
注:本文使用 Maven 为例,Gradle 用户可以参照上方的官方文档
...
<properties>
<org.mapstruct.version>1.5.5.Final</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> <!-- depending on your project -->
<target>1.8</target> <!-- depending on your project -->
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
<!-- other annotation processors -->
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
示例
官方示例代码:mapstruct-examples/mapstruct-mapper-repo at main · mapstruct/mapstruct-examples (github.com)[9]
我们可以直接单独下载上面对应 demo 的项目文件夹,具体方法参照文末 补充 章节
您也可以自行创建 Spring 项目,然后依次安装 MapStruct 后,创建如下文件
Car.java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Car {
private String make;
private int numberOfSeats;
private CarType type;
}
CarType.java
public enum CarType {
SPORTS, OTHER;
}
CarDto.java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CarDto {
private String make;
private int seatCount;
private String type;
}
CarMapper.java
@Mapper
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper( CarMapper.class );
@Mapping(source = "numberOfSeats", target = "seatCount")
CarDto carToCarDto(Car car);
}
TestMapperRepo.java
public class TestMapperRepo {
@Test
public void shouldMapCarToDto() {
//given
Car car = new Car("Morris", 5, CarType.SPORTS);
//when
CarDto carDto = CarMapper.INSTANCE.carToCarDto(car);
//then
assertThat(carDto).isNotNull();
assertThat(carDto.getMake()).isEqualTo("Morris");
assertThat(carDto.getSeatCount()).isEqualTo(5);
assertThat(carDto.getType()).isEqualTo("SPORTS");
}
}
但是 MapStruct 默认是和 Lombok 冲突的,无法识别,会出现属性找不到错误,您可以将 Lombok 注释替换为对应代码,或者是 pom.xml 替换如下
<properties>
<org.projectlombok.version>1.18.16</org.projectlombok.version>
<org.mapstruct.version>1.5.5.Final</org.mapstruct.version>
<lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version>
</properties>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${org.projectlombok.version}</version>
</dependency>
<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.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${org.projectlombok.version}</version>
</path>
<!-- This is needed when using Lombok 1.18.16 and above -->
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>${lombok-mapstruct-binding.version}</version>
</path>
<!-- Mapstruct should follow the lombok path(s) -->
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
我们需要确保 Lombok 最低版本为 1.18.16,同时 annotationProcessorPaths 中,mapstruct-processor 的配置要在 lombok 之后
补充
POJO

简介
POJO(Plain Old Java Object) 字面翻译为 “纯洁老式的 Java 对象”,但是其更加通俗的名称为 “简单 Java 对象”
内在含义:不继承或不实现任何其它 Java 框架的类或接口,没有被其它框架侵入的 Java 对象
注:里面的类和接口仅指的是其它 Java 框架中的类和接口,而不是所有类和接口
相关链接:
-
POJO (martinfowler.com)[10] -
Plain old Java object – Wikipedia[11]
转换对象
我们可以辅助理解 POJO 是中间对象
该中间对象可以根据不同情况转换为 PO、DTO、VO
1 .POJO 持久化之后 –> PO(Persistent Object)
2 .POJO 传输过程中 –> DTO(Data Transfer Object)
3 .POJO 用作表示层 –> VO(View Object)
4 .POJO 用作业务逻辑层 –> BO(Business Object)
注:BO 主要作用是把业务逻辑封装为对象,这个对象可以包括一个或多个其它的对象,BO 通过调用 DAO 方法,结合 PO,VO 进行业务操作
GitHub 单独下载文件夹
单独下载文件不必多说,我们点击进入 GitHub 的文件之后右上角会有 raw 文件下载按钮,点击即可下载源文件
如果是下载文件夹的话,有以下方式
GitZip
下载谷歌插件 GitZip
链接:GitZip for github – Chrome 应用商店 (google.com)[12]
安装之后我们双击对应的文件夹,然后点击右下角的下载按钮即可下载

DownGit
网站地址:DownGit (minhaskamal.github.io)[13]
参考链接
-
Quick Guide to MapStruct | Baeldung[14] -
POJO、PO、DTO、VO、BO ? EJB、EntityBean[15] -
什么是 JavaBean? – JYRoy – 博客园 (cnblogs.com)[16] -
JavaBean – 廖雪峰的官方网站 (liaoxuefeng.com)[17] -
微服务中 VO、DTO、Entity 间的相互转换处理[18] -
Github | 如何在 Github 上只下载一个文件或文件夹[19]
参考资料
Dozer – Usage (sourceforge.net): https://dozer.sourceforge.net/documentation/gettingstarted.html
[2]
Orika reference guide (orika-mapper.github.io): https://orika-mapper.github.io/orika-docs/
[3]
ModelMapper – Simple, Intelligent, Object Mapping.: http://modelmapper.org/
[4]
MapStruct – Java bean mappings, the easy way!: https://mapstruct.org/
[5]
JMapper Framework (jmapper-framework.github.io): http://jmapper-framework.github.io/jmapper-core/
[6]
mapstruct/mapstruct: An annotation processor for generating type-safe bean mappers (github.com): https://github.com/mapstruct/mapstruct
[7]
MapStruct – Java bean mappings, the easy way!: https://mapstruct.org/
[8]
Installation – MapStruct: https://mapstruct.org/documentation/installation/
[9]
mapstruct-examples/mapstruct-mapper-repo at main · mapstruct/mapstruct-examples (github.com): https://github.com/mapstruct/mapstruct-examples/tree/main/mapstruct-mapper-repo
[10]
POJO (martinfowler.com): https://martinfowler.com/bliki/POJO.html
[11]
Plain old Java object – Wikipedia: https://en.wikipedia.org/wiki/Plain_old_Java_object
[12]
GitZip for github – Chrome 应用商店 (google.com): https://chrome.google.com/webstore/detail/gitzip-for-github/ffabmkklhbepgcgfonabamgnfafbdlkn
[13]
DownGit (minhaskamal.github.io): https://minhaskamal.github.io/DownGit/#/home
[14]
Quick Guide to MapStruct | Baeldung: https://www.baeldung.com/mapstruct
[15]
POJO、PO、DTO、VO、BO ? EJB、EntityBean: https://www.cnblogs.com/panchanggui/p/11610998.html
[16]
什么是 JavaBean? – JYRoy – 博客园 (cnblogs.com): https://www.cnblogs.com/jyroy/p/11102298.html
[17]
JavaBean – 廖雪峰的官方网站 (liaoxuefeng.com): https://www.liaoxuefeng.com/wiki/1252599548343744/1260474416351680
[18]
微服务中 VO、DTO、Entity 间的相互转换处理: http://www.crazy1984.com/2020/04/dev/20200409_micros_dto-entity-mapping/
[19]
Github | 如何在 Github 上只下载一个文件或文件夹: https://blog.csdn.net/m0_72224305/article/details/127567759
原文始发于微信公众号(编程杂烩):MapStruct POJO 映射框架指南
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/226012.html