MapStruct物件轉換
第一次看到MapStruct
的時候, 我個人非常的開心。因為其跟我內心裡面的想法不謀而合。
1 MapStruct 是什麼?
1.1 JavaBean 的困擾
對於程式碼中
之間的轉換, 一直是困擾我很久的事情。JavaBean
在開發的時候我看到業務程式碼之間有很多的
之間的相互轉化, 非常的影響觀感, 卻又不得不存在。我後來想的一個辦法就是通過反射, 或者自己寫很多的轉換器。JavaBean
第一種通過反射的方法確實比較方便, 但是現在無論是BeanUtils
,BeanCopier
等在使用反射的時候都會影響到效能。雖然我們可以進行反射資訊的快取來提高效能。
但是像這種的話, 需要型別和名稱都一樣才會進行對映, 有很多時候, 由於不同的團隊之間使用的名詞不一樣, 還是需要很多的手動 set/get 等功能。
第二種的話就是會很浪費時間, 而且在新增新的欄位的時候也要進行方法的修改。不過, 由於不需要進行反射, 其效能是很高的。
1.2MapStruct
帶來的改變
MapSturct
是一個生成型別安全, 高效能且無依賴的JavaBean對映程式碼的註解處理器(annotation processor)。
抓一下重點:
-
註解處理器
-
可以生成
JavaBean
之間那的對映程式碼 -
型別安全, 高效能, 無依賴性
從字面的理解, 我們可以知道, 該工具可以幫我們實現
之間的轉換, 通過註解的方式。JavaBean
同時, 作為一個工具類,相比於手寫, 其應該具有便捷, 不容易出錯的特點。
2MapStruct
入門
入門很簡單。我是基於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
publicclassOrder{
/**
*訂單id
*/
privateLongid;
/**
*訂單編號
*/
privateStringorderSn;
/**
*收貨人姓名/號碼
*/
privateStringreceiverKeyword;
/**
*訂單狀態:0->待付款;1->待發貨;2->已發貨;3->已完成;4->已關閉;5->無效訂單
*/
privateIntegerstatus;
/**
*訂單型別:0->正常訂單;1->秒殺訂單
*/
privateIntegerorderType;
/**
*訂單來源:0->PC訂單;1->app訂單
*/
privateIntegersourceType;
}
對應的查詢引數
@Data
publicclassOrderQueryParam{
/**
*訂單編號
*/
privateStringorderSn;
/**
*收貨人姓名/號碼
*/
privateStringreceiverKeyword;
/**
*訂單狀態:0->待付款;1->待發貨;2->已發貨;3->已完成;4->已關閉;5->無效訂單
*/
privateIntegerstatus;
/**
*訂單型別:0->正常訂單;1->秒殺訂單
*/
privateIntegerorderType;
/**
*訂單來源:0->PC訂單;1->app訂單
*/
privateIntegersourceType;
}
2.3寫 Mapper
Mapper
即對映器, 一般來說就是寫xxxMapper
介面。
當然, 不一定是以Mapper
結尾的。只是官方是這麼寫的。在本入門例子中,對應的介面如下
importcom.homejim.mapstruct.dto.OrderQueryParam;
importcom.homejim.mapstruct.entity.Order;
importorg.mapstruct.Mapper;
importorg.mapstruct.Mapping;
@Mapper
publicinterfaceOrderMapper{
OrderQueryParamentity2queryParam(Orderorder);
}
簡單的對映(欄位和型別都匹配), 只有一個要求, 在介面上寫@Mapper
註解即可。
然後方法上, 入參對應要被轉化的物件, 返回值對應轉化後的物件, 方法名稱可任意。
2.4 測試
寫一個測試類測試一下。
@Test
publicvoidentity2queryParam(){
Orderorder=newOrder();
order.setId(12345L);
order.setOrderSn("orderSn");
order.setOrderType(0);
order.setReceiverKeyword("keyword");
order.setSourceType(1);
order.setStatus(2);
OrderMappermapper=Mappers.getMapper(OrderMapper.class);
OrderQueryParamorderQueryParam=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 裡可以看到。
生成的程式碼
對應的程式碼
@Generated(
value="org.mapstruct.ap.MappingProcessor",
date="2019-08-02T00:29:49+0800",
comments="version:1.3.0.Final,compiler:javac,environment:Java11.0.2(OracleCorporation)"
)
publicclassOrderMapperImplimplementsOrderMapper{
@Override
publicOrderQueryParamentity2queryParam(Orderorder){
if(order==null){
returnnull;
}
OrderQueryParamorderQueryParam=newOrderQueryParam();
orderQueryParam.setOrderSn(order.getOrderSn());
orderQueryParam.setReceiverKeyword(order.getReceiverKeyword());
orderQueryParam.setStatus(order.getStatus());
orderQueryParam.setOrderType(order.getOrderType());
orderQueryParam.setSourceType(order.getSourceType());
returnorderQueryParam;
}
}
可以看到其生成了一個實現類, 而程式碼也類似於我們手寫, 通俗易懂。
3.2 易於 debug
在我們生成的程式碼中, 我們可以輕易的進行 debug。
易於 DEBUG
在使用反射的時候, 如果出現了問題, 很多時候是很難找到是什麼原因的。
3.3 使用相對簡單
如果是完全對映的, 使用起來肯定沒有反射簡單。用類似BeanUtils
這些工具一條語句就搞定了。但是,如果需要進行特殊的匹配(特殊型別轉換, 多對一轉換等), 其相對來說也是比較簡單的。
基本上, 使用的時候, 我們只需要宣告一個介面, 介面下寫對應的方法, 就可以使用了。當然, 如果有特殊情況, 是需要額外處理的。
3.4 程式碼獨立
生成的程式碼是對立的, 沒有執行時的依賴。