還在用 BeanUtils來作對象轉換嗎?快試試 MapStruct吧


做者:阿進的寫字檯java

https://www.cnblogs.com/homejim/git

第一次看到 MapStruct 的時候, 我我的很是的開心。由於其跟我心裏裏面的想法不謀而合。github

1 MapStruct 是什麼?

1.1 JavaBean 的困擾

對於代碼中 JavaBean之間的轉換, 一直是困擾我好久的事情。web

在開發的時候我看到業務代碼之間有不少的 JavaBean 之間的相互轉化, 很是的影響觀感, 卻又不得不存在。我後來想的一個辦法就是經過反射, 或者本身寫不少的轉換器。面試

第一種經過反射的方法確實比較方便, 可是如今不管是 BeanUtils, BeanCopier 等在使用反射的時候都會影響到性能。雖然咱們能夠進行反射信息的緩存來提升性能。緩存

可是像這種的話, 須要類型和名稱都同樣纔會進行映射, 有不少時候, 因爲不一樣的團隊之間使用的名詞不同, 仍是須要不少的手動 set/get 等功能。安全

第二種的話就是會很浪費時間, 並且在添加新的字段的時候也要進行方法的修改。不過, 因爲不須要進行反射, 其性能是很高的。微信

1.2 MapStruct 帶來的改變

MapSturct 是一個生成類型安全, 高性能且無依賴的 JavaBean 映射代碼的註解處理器(annotation processor)。app

抓一下重點:編輯器

  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。

易於 DEBUG


在使用反射的時候, 若是出現了問題, 不少時候是很難找到是什麼緣由的。

3.3 使用相對簡單

若是是徹底映射的, 使用起來確定沒有反射簡單。用相似 BeanUtils 這些工具一條語句就搞定了。可是,若是須要進行特殊的匹配(特殊類型轉換, 多對一轉換等), 其相對來講也是比較簡單的。

基本上, 使用的時候, 咱們只須要聲明一個接口, 接口下寫對應的方法, 就可使用了。固然, 若是有特殊狀況, 是須要額外處理的。

3.4 代碼獨立

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


--------------------------   END  --------------------------


原創整理《第2版:互聯網大廠面試題》



掃碼添加,個人我的微信,備註: 面試題
 注意,不要亂回覆 
(必定要備註: 面試題 )不然不給經過

若是被添加頻繁,請30分鐘後再試試

沒錯,加我好友,給你安排到位

本文分享自微信公衆號 - Java研發軍團(ityuancheng)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索