优雅的对象转换解决方案-MapStruct及其入门(一)

第一次看到 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 代码独立

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



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

原文地址:https://www.cnblogs.com/homejim/p/11306313.html

时间: 2024-10-02 00:53:53

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

对象转换利器之Dozer

在Java的世界中,经常会涉及到需要在2个对象中进行转换,比如说: 调用SOAP Web服务,需要把自己的Domain对象转换为Soap服务的Jaxb对象请求, 在分层级SOA架构中,2个层级之间Domain对象的转换, 在分布式系统中,每个模块使用自己的领域对象,防止自己的业务被其他模块的domain对象影响 通常情况下,程序员们会自己弄一个Transformer层,来做对象之间的转换.估计很多同志在自己的项目代码中都可以看到Transformer的身影.但是,复杂的Java对象之间的转换并不

python 将类对象转换成json

如果将字典转换成json,想必都很熟悉了,如果在进阶点,将class类转换成json对象该如何操作了? 1,先定义一个类 #定义一个Student类 class Student(object): def __init__(self,name,age,score): self.name = name self.age = age self.score = score 2,在实例化Student类,传入3个参数 #实例化这个对象 s = Student('hello',20,80) 3,利用json

C++习题 对象转换

[Submit][Status][Web Board] Description 定义一个Teacher(教师)类(教师号,姓名,性别,薪金)和一个Student(学生)类(学号,姓名,性别,成绩),二者有一部分数据成员是相同的,num(号码),name(姓名),sex(性别).编写程序,将一个Student对象(学生)转换为Teacher(教师)类,只将以上3个相同的数据成员移植过去.可以设想为: 一位学生大学毕业了,留校担任教师,他原有的部分数据对现在的教师身份来说仍然是有用的,应当保留并成为

(精华)将json数组和对象转换成List和Map(小龙哥和牛徳鹤的对话)

将java标准的数据结构ArrayList和HashMap转换成json对象和数组很简单 只需要JSONArray.fromObject(obj);或者JSONObject.fromObject(obj); 将json对象转换成Map(必须用到遍历) public static void main(String[] args){ HashMap<String, Object> map = new HashMap<String, Object>(); map.put("na

对象转换处理

#region 对象转换处理 2 /// <summary> 3 /// 判断对象是否为Int32类型的数字 4 /// </summary> 5 /// <param name="Expression"></param> 6 /// <returns></returns> 7 public static bool IsNumeric(object expression) 8 { 9 if (expression

JSON对象转换成JSON字符串

1.问题背景 有一个json对象,需要将其转换成json字符串 JSON.stringify(obj) 2.实现源码 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtm

将Model对象转换成json文本或者json二进制文件

https://github.com/casatwy/AnyJson 注意:经过测试,不能够直接处理字典或者数组 主要源码的注释 AJTransformer.h 与 AJTransformer.m // // AJTransformer.h // AnyJson // // Created by casa on 14-9-19. // Copyright (c) 2014年 casa. All rights reserved. // #import <Foundation/Foundation.

将list&lt;对象&gt;转换成DataTable,把DataTable转换成参数传入存储过程实现批量插入数据

领导让在存储过程中批量添加数据,找出效率最高的,我看到后台代码后,发现可以将list<对象>转换成DataTable,把DataTable转换成参数传入存储过程实现批量插入数据,知道还有其他的方法,不过这个方法已经实现,就写一下了: 1.创建表. CREATE TABLE [dbo].[person]( [ID] [int] IDENTITY(1,1) NOT NULL, [Name] [nvarchar](50) NULL, [Pwd] [nvarchar](50) NULL, [Age]

jquery 和 js 对象转换

核心提示:jquery选择器得到的jquery对象和标准的 javascript中的document.getElementById()取得的dom对象是两种不同的对象类型,一般情况下,如S('#id')得到的是jquery对象,它不能使用js中的dom方法.所以,如果jquery对象要使用标准的dom方法,就需要进行对象转换. 刚开始学习jQuery,可能一时会分不清楚哪些是jQuery对象,哪些是DOM对象.至于DOM对象不多解释,我们接触的太多了,下面重点介绍一下jQuery,以及两者相互间