天天看點

優雅的對象轉換解決方案-MapStruct及其入門(一)1 MapStruct 是什麼?2 MapStruct 入門3 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及其入門(一)1 MapStruct 是什麼?2 MapStruct 入門3 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及其入門(一)1 MapStruct 是什麼?2 MapStruct 入門3 MapStruct 分析

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

3.3 使用相對簡單

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

BeanUtils

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

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

3.4 代碼獨立

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

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

繼續閱讀