天天看點

優雅的對象轉換解決方案-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個步驟來實作了從

Order

OrderQueryParam

的轉換。

那麼, 作為一個注解處理器, 通過

MapStruct

生成的代碼具有怎麼樣的優勢呢?

3.1 高性能

這是相對反射來說的, 反射需要去讀取位元組碼的内容, 花銷會比較大。 而通過

MapStruct

來生成的代碼, 其類似于人手寫。 速度上可以得到保證。

前面例子中生成的代碼可以在編譯後看到。 在 target/generated-sources/annotations 裡可以看到。

優雅的對象轉換解決方案-MapStruct及其入門(一)

對應的代碼

@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。

優雅的對象轉換解決方案-MapStruct及其入門(一)

在使用反射的時候, 如果出現了問題, 很多時候是很難找到是什麼原因的。

3.3 使用相對簡單

如果是完全映射的, 使用起來肯定沒有反射簡單。 用類似

BeanUtils

這些工具一條語句就搞定了。 但是,如果需要進行特殊的比對(特殊類型轉換, 多對一轉換等), 其相對來說也是比較簡單的。

基本上, 使用的時候, 我們隻需要聲明一個接口, 接口下寫對應的方法, 就可以使用了。 當然, 如果有特殊情況, 是需要額外處理的。

3.4 代碼獨立

生成的代碼是對立的, 沒有運作時的依賴。

入門就先到這裡, 後續會講一些進階一點的用法。

作者:阿進的寫字台

出處:https://www.cnblogs.com/homejim/

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。