第一次看到 MapStruct 的时候, 我个人非常的开心。 因为其跟我内心里面的想法不谋而合。

1 MapStruct 是什么?

1.1 JavaBean 的困扰

对于代码中 JavaBean之间的转换, 一直是困扰我很久的事情。 在开发的时候我看到业务代码之间有很多的 JavaBean 之间的相互转化, 非常的影响观感, 却又不得不存在。 我后来想的一个办法就是通过反射, 或者自己写很多的转换器。

第一种通过反射的方法确实比较方便, 但是现在无论是 BeanUtils, BeanCopier 等在使用反射的时候都会影响到性能。 虽然我们可以进行反射信息的缓存来提高性能。 但是像这种的话, 需要类型和名称都一样才会进行映射, 有很多时候, 由于不同的团队之间使用的名词不一样, 还是需要很多的手动 set/get 等功能。

第二种的话就是会很浪费时间, 而且在添加新的字段的时候也要进行方法的修改。 不过, 由于不需要进行反射, 其性能是很高的。

1.2 MapStruct 带来的改变

MapSturct 是一个生成类型安全, 高性能且无依赖的 JavaBean 映射代码的注解处理器(annotation processor)。

抓一下重点:

  1. 注解处理器
  2. 可以生成 JavaBean 之间那的映射代码
  3. 类型安全, 高性能, 无依赖性

从字面的理解, 我们可以知道, 该工具可以帮我们实现 JavaBean 之间的转换, 通过注解的方式。

同时, 作为一个工具类,相比于手写, 其应该具有便捷, 不容易出错的特点。

2 MapStruct 入门

入门很简单。 我是基于 Maven 来进行项目 jar 包管理的。

2.1 引入依赖

<properties>
<org.mapstruct.version>1.3.0.Final</org.mapstruct.version>
</properties> <dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>

2.2 创建entity和dto对象

该类是从 github 某个订单系统里面拿下来的部分。

@Data
public class Order { /**
*订单id
*/
private Long id; /**
* 订单编号
*/
private String orderSn; /**
* 收货人姓名/号码
*/
private String receiverKeyword; /**
* 订单状态:0->待付款;1->待发货;2->已发货;3->已完成;4->已关闭;5->无效订单
*/
private Integer status; /**
* 订单类型:0->正常订单;1->秒杀订单
*/
private Integer orderType; /**
* 订单来源:0->PC订单;1->app订单
*/
private Integer sourceType;
}

对应的查询参数

@Data
public class OrderQueryParam {
/**
* 订单编号
*/
private String orderSn; /**
* 收货人姓名/号码
*/
private String receiverKeyword; /**
* 订单状态:0->待付款;1->待发货;2->已发货;3->已完成;4->已关闭;5->无效订单
*/
private Integer status; /**
* 订单类型:0->正常订单;1->秒杀订单
*/
private Integer orderType; /**
* 订单来源:0->PC订单;1->app订单
*/
private Integer sourceType; }

2.3 写 Mapper

Mapper 即映射器, 一般来说就是写 xxxMapper 接口。 当然, 不一定是以 Mapper 结尾的。 只是官方是这么写的。 在本入门例子中,对应的接口如下

import com.homejim.mapstruct.dto.OrderQueryParam;
import com.homejim.mapstruct.entity.Order;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping; @Mapper
public interface OrderMapper { OrderQueryParam entity2queryParam(Order order); }

简单的映射(字段和类型都匹配), 只有一个要求, 在接口上写 @Mapper 注解即可。 然后方法上, 入参对应要被转化的对象, 返回值对应转化后的对象, 方法名称可任意。

2.4 测试

写一个测试类测试一下。

    @Test
public void entity2queryParam() {
Order order = new Order();
order.setId(12345L);
order.setOrderSn("orderSn");
order.setOrderType(0);
order.setReceiverKeyword("keyword");
order.setSourceType(1);
order.setStatus(2); OrderMapper mapper = Mappers.getMapper(OrderMapper.class);
OrderQueryParam orderQueryParam = mapper.entity2queryParam(order);
assertEquals(orderQueryParam.getOrderSn(), order.getOrderSn());
assertEquals(orderQueryParam.getOrderType(), order.getOrderType());
assertEquals(orderQueryParam.getReceiverKeyword(), order.getReceiverKeyword());
assertEquals(orderQueryParam.getSourceType(), order.getSourceType());
assertEquals(orderQueryParam.getStatus(), order.getStatus()); }

测试通过, 没有任何的问题。

3 MapStruct 分析

上面中, 我写了3个步骤来实现了从 OrderOrderQueryParam 的转换。

那么, 作为一个注解处理器, 通过MapStruct 生成的代码具有怎么样的优势呢?

3.1 高性能

这是相对反射来说的, 反射需要去读取字节码的内容, 花销会比较大。 而通过 MapStruct 来生成的代码, 其类似于人手写。 速度上可以得到保证。

前面例子中生成的代码可以在编译后看到。 在 target/generated-sources/annotations 里可以看到。

对应的代码

@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2019-08-02T00:29:49+0800",
comments = "version: 1.3.0.Final, compiler: javac, environment: Java 11.0.2 (Oracle Corporation)"
)
public class OrderMapperImpl implements OrderMapper { @Override
public OrderQueryParam entity2queryParam(Order order) {
if ( order == null ) {
return null;
} OrderQueryParam orderQueryParam = new OrderQueryParam(); orderQueryParam.setOrderSn( order.getOrderSn() );
orderQueryParam.setReceiverKeyword( order.getReceiverKeyword() );
orderQueryParam.setStatus( order.getStatus() );
orderQueryParam.setOrderType( order.getOrderType() );
orderQueryParam.setSourceType( order.getSourceType() ); return orderQueryParam;
}
}

可以看到其生成了一个实现类, 而代码也类似于我们手写, 通俗易懂。

3.2 易于 debug

在我们生成的代码中, 我们可以轻易的进行 debug。

在使用反射的时候, 如果出现了问题, 很多时候是很难找到是什么原因的。

3.3 使用相对简单

如果是完全映射的, 使用起来肯定没有反射简单。 用类似 BeanUtils 这些工具一条语句就搞定了。 但是,如果需要进行特殊的匹配(特殊类型转换, 多对一转换等), 其相对来说也是比较简单的。

基本上, 使用的时候, 我们只需要声明一个接口, 接口下写对应的方法, 就可以使用了。 当然, 如果有特殊情况, 是需要额外处理的。

3.4 代码独立

生成的代码是对立的, 没有运行时的依赖。


入门就先到这里, 后续会讲一些高级一点的用法。

优雅的对象转换解决方案-MapStruct及其入门(一)的更多相关文章

  1. 优雅的对象转换解决方案-MapStruct使用进阶(二)

    在前面, 介绍了 MapStruct 及其入门. 本文则是进一步的进阶. 在 MapStruct 生成对应的实现类的时候, 有如下的几个情景. 1 属性名称相同,则进行转化 在实现类的时候, 如果属性 ...

  2. 对象转换工具 MapStruct 介绍

    前言 在我们日常开发的分层结构的应用程序中,为了各层之间互相解耦,一般都会定义不同的对象用来在不同层之间传递数据,因此,就有了各种 XXXDTO.XXXVO.XXXBO 等基于数据库对象派生出来的对象 ...

  3. 对象拷贝 - 优雅的解决方案 Mapstruct

    MapStruct GitHub 访问地址 : https://github.com/mapstruct/mapstruct/ 使用例子 : https://github.com/mapstruct/ ...

  4. 使用XStream是实现XML与Java对象的转换(1)--简介及入门示例

    一.简单介绍 XStream是thoughtworks开发的开源框架,用于实现XML数据于Java对象.Json数据的转换.它不需要schema或其他的mapping文件就可以进行java对象和xml ...

  5. mapstruct解放Java对象转换

    摘要 当前web后端开发,都是使用多层工程结构,需要在VO,BO,DTO,DO等各种数据结构中相互转换.这些转换代码都是些比较简单的字段映射,类型转换,重复性工作比较高,可以使用一些工具解放我们的双手 ...

  6. 对象转换利器之Dozer

    什么是Dozer Dozer是一个Java对象转换工具,可以在JavaBean和JavaBean之间进行递归数据复制,并且适应不同复杂的类型.Dozer会直接将名称相同的属性进行复制,属性名不同或者有 ...

  7. java对象转换

    对象转换: 对象的分层涉及到各个层级之间的对象转换(Entity2DTO , DTO2VO, VO2DTO,DTO2Entity等),传统的采用set/get 方法硬编码实现写的代码比较多:或者采用B ...

  8. 我写了个IDEA开源插件,vo2dto 一键生成对象转换

    让人头疼的对象转换 头炸,po2vo.vo2do.do2dto,一堆对象属性,取出来塞进来.要不是为了 DDD 架构下的各个分层防腐,真想一竿子怼下去. 那上 BeanUtils.copyProper ...

  9. 采用Lambda表达式快速实现实体模型对象转换到DTO

    在项目中,采用code first时建立的模型对象不能直接用于数据传输,需要从新根据需求建立Dto对象 为什么需要建立Dto对象呢? DTO即数据传输对象.之前不明白有些框架中为什么要专门定义DTO来 ...

随机推荐

  1. ajax 的登录认证

    在models中 先创建一个表 from django.db import models # Create your models here. class UserInfo(models.Model) ...

  2. Vue快速学习_第一节

    之前写CRM都是Django前后端一起写的,在大部分项目中实际上前后端是分离的,因此我们需要学习一个前端框架来进行前端页面的编写,这里选择了Vue进行学习,好了开始学习吧. 1.ES6部分知识点学习 ...

  3. 2019.6.21 NOIP2018提高组模拟题(二)

    1.咒语 (curse.pas/c/cpp) [题目描述] 亮亮梦到自己来到了魔法城堡,但一扇巨大的石门阻拦了他通向城堡内的路.正当他沮丧之际,突然发现门上有一处机关,机关上有一张很长的纸条.亮亮拿起 ...

  4. samba搭建与配置说明

    1. 环境检查getenforce 检查selinux service iptables stop 2.安装samba yum -y install samba 3.配置samba /etc/samb ...

  5. HTML表单部分学习笔记

    第一部分: <!-- action :指定处理提交表单的格式 method :指定提交表单的http方法 enctype:指明用来把表单提交给服务器时的互联网媒体形式 --> <fo ...

  6. Java volatile关键字小结

    public class Test { public static void main(String[] args){ } } /* 12.3 Java内存模型 Java内存模型定义了线程与主内存之间 ...

  7. nu.xom:Document

    Document: 机翻 Document(Document doc):创建此文档的副本. Document(Element root):使用指定的根元素创建新的Document对象. DocType ...

  8. py+selenium 无法定位ShowModalDialog模态窗口【已解决】

    问题:无法定位弹出的模态窗口. 前瞻: 模态窗口:关闭之前,无法操作其他窗口. 但是selenium无法定位到这类窗口,百度说是目前selenium不支持处理模态窗口. 目标:定位到窗口里面的元素,完 ...

  9. 掌握简单的Makefile文件编程

    Makefile描述整个程序的编译.链接规则 其中还包括了工程中用到的那些源文件及需要产生的目标文件 1)Makefile编程规则 目标(唯一):依赖(可多个) 命令... 伪目标 .PHONY:cl ...

  10. NOIp2018 TG day1 T2暨洛谷P5020 货币系统:题解

    题目链接:https://www.luogu.org/problemnew/show/P5020 这道题感觉比较水啊,身为普及组蒟蒻都不费力的做出来了,而且数据范围应该还能大一些,n起码几万几十万都不 ...