優(yōu)雅的對(duì)象轉(zhuǎn)換解決方案稽煤,為什么更推薦 MapStruct 呢?

第一次看到 MapStruct 的時(shí)候轧简, 我個(gè)人非常的開心吉懊。因?yàn)槠涓覂?nèi)心里面的想法不謀而合假勿。

1 MapStruct 是什么?

1.1 JavaBean 的困擾

對(duì)于代碼中 JavaBean之間的轉(zhuǎn)換恶导, 一直是困擾我很久的事情浸须。在開發(fā)的時(shí)候我看到業(yè)務(wù)代碼之間有很多的 JavaBean 之間的相互轉(zhuǎn)化, 非常的影響觀感裂垦, 卻又不得不存在蕉拢。我后來想的一個(gè)辦法就是通過反射, 或者自己寫很多的轉(zhuǎn)換器午乓。整理了一份Java面試寶典完整版PDF已整理成文檔

第一種通過反射的方法確實(shí)比較方便闸准, 但是現(xiàn)在無論是 BeanUtils, BeanCopier 等在使用反射的時(shí)候都會(huì)影響到性能夷家。雖然我們可以進(jìn)行反射信息的緩存來提高性能。但是像這種的話枣接, 需要類型和名稱都一樣才會(huì)進(jìn)行映射缺谴, 有很多時(shí)候湿蛔, 由于不同的團(tuán)隊(duì)之間使用的名詞不一樣县爬, 還是需要很多的手動(dòng) set/get 等功能。

第二種的話就是會(huì)很浪費(fèi)時(shí)間察迟, 而且在添加新的字段的時(shí)候也要進(jìn)行方法的修改扎瓶。不過泌枪, 由于不需要進(jìn)行反射, 其性能是很高的误证。

1.2 MapStruct 帶來的改變

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

抓一下重點(diǎn):

  1. 注解處理器

  2. 可以生成 JavaBean 之間那的映射代碼

  3. 類型安全灌具, 高性能像棘, 無依賴性

從字面的理解缕题, 我們可以知道, 該工具可以幫我們實(shí)現(xiàn) JavaBean 之間的轉(zhuǎn)換瘪松, 通過注解的方式锨阿。

同時(shí), 作為一個(gè)工具類壳嚎,相比于手寫末早, 其應(yīng)該具有便捷然磷, 不容易出錯(cuò)的特點(diǎn)。

2 MapStruct 入門

入門很簡(jiǎn)單寡润。我是基于 Maven 來進(jìn)行項(xiàng)目 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 創(chuàng)建entity和dto對(duì)象

該類是從 github 某個(gè)訂單系統(tǒng)里面拿下來的部分栗柒。

@Data
public class Order {

    /**
     *訂單id
     */
    private Long id;

    /**
     * 訂單編號(hào)
     */
    private String orderSn;

    /**
     * 收貨人姓名/號(hào)碼
     */
    private String receiverKeyword;

    /**
     * 訂單狀態(tài):0->待付款;1->待發(fā)貨太伊;2->已發(fā)貨逛钻;3->已完成曙痘;4->已關(guān)閉;5->無效訂單
     */
    private Integer status;

    /**
     * 訂單類型:0->正常訂單名扛;1->秒殺訂單
     */
    private Integer orderType;

    /**
     * 訂單來源:0->PC訂單茧痒;1->app訂單
     */
    private Integer sourceType;
}

對(duì)應(yīng)的查詢參數(shù)

@Data
public class OrderQueryParam {
    /**
     * 訂單編號(hào)
     */
    private String orderSn;

    /**
     * 收貨人姓名/號(hào)碼
     */
    private String receiverKeyword;

    /**
     * 訂單狀態(tài):0->待付款旺订;1->待發(fā)貨;2->已發(fā)貨拘领;3->已完成约素;4->已關(guān)閉笆凌;5->無效訂單
     */
    private Integer status;

    /**
     * 訂單類型:0->正常訂單菩颖;1->秒殺訂單
     */
    private Integer orderType;

    /**
     * 訂單來源:0->PC訂單为障;1->app訂單
     */
    private Integer sourceType;

}

2.3 寫 Mapper

Mapper 即映射器鳍怨, 一般來說就是寫 xxxMapper 接口。當(dāng)然声滥, 不一定是以 Mapper 結(jié)尾的。只是官方是這么寫的纽疟。在本入門例子中憾赁,對(duì)應(yīng)的接口如下

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);

}

簡(jiǎn)單的映射(字段和類型都匹配)龙考, 只有一個(gè)要求, 在接口上寫 @Mapper 注解即可炎功。然后方法上蛇损, 入?yún)?duì)應(yīng)要被轉(zhuǎn)化的對(duì)象肛宋, 返回值對(duì)應(yīng)轉(zhuǎn)化后的對(duì)象酝陈, 方法名稱可任意。

2.4 測(cè)試

寫一個(gè)測(cè)試類測(cè)試一下锈死。

@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());

}

測(cè)試通過, 沒有任何的問題穆壕。

3 MapStruct 分析

上面中待牵, 我寫了3個(gè)步驟來實(shí)現(xiàn)了從 Order 到 OrderQueryParam 的轉(zhuǎn)換。

那么喇勋, 作為一個(gè)注解處理器缨该, 通過MapStruct 生成的代碼具有怎么樣的優(yōu)勢(shì)呢?

3.1 高性能

這是相對(duì)反射來說的川背, 反射需要去讀取字節(jié)碼的內(nèi)容贰拿, 花銷會(huì)比較大。而通過 MapStruct 來生成的代碼熄云, 其類似于人手寫膨更。速度上可以得到保證缴允。

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

image

生成的代碼

對(duì)應(yīng)的代碼

@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;
    }
}

可以看到其生成了一個(gè)實(shí)現(xiàn)類, 而代碼也類似于我們手寫矗漾, 通俗易懂锈候。

3.2 易于 debug

在我們生成的代碼中, 我們可以輕易的進(jìn)行 debug缩功。

image

易于 DEBUG

在使用反射的時(shí)候晴及, 如果出現(xiàn)了問題, 很多時(shí)候是很難找到是什么原因的嫡锌。

3.3 使用相對(duì)簡(jiǎn)單

如果是完全映射的虑稼, 使用起來肯定沒有反射簡(jiǎn)單。用類似 BeanUtils 這些工具一條語句就搞定了势木。但是蛛倦,如果需要進(jìn)行特殊的匹配(特殊類型轉(zhuǎn)換, 多對(duì)一轉(zhuǎn)換等)啦桌, 其相對(duì)來說也是比較簡(jiǎn)單的溯壶。整理了一份Java面試寶典完整版PDF已整理成文檔

基本上, 使用的時(shí)候甫男, 我們只需要聲明一個(gè)接口且改, 接口下寫對(duì)應(yīng)的方法, 就可以使用了板驳。當(dāng)然又跛, 如果有特殊情況, 是需要額外處理的若治。

3.4 代碼獨(dú)立

生成的代碼是對(duì)立的慨蓝, 沒有運(yùn)行時(shí)的依賴。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末端幼,一起剝皮案震驚了整個(gè)濱河市礼烈,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌婆跑,老刑警劉巖此熬,帶你破解...
    沈念sama閱讀 221,198評(píng)論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異滑进,居然都是意外死亡摹迷,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門郊供,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人近哟,你說我怎么就攤上這事驮审。” “怎么了?”我有些...
    開封第一講書人閱讀 167,643評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵疯淫,是天一觀的道長(zhǎng)地来。 經(jīng)常有香客問我,道長(zhǎng)熙掺,這世上最難降的妖魔是什么未斑? 我笑而不...
    開封第一講書人閱讀 59,495評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮币绩,結(jié)果婚禮上蜡秽,老公的妹妹穿的比我還像新娘。我一直安慰自己缆镣,他們只是感情好芽突,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著董瞻,像睡著了一般寞蚌。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上钠糊,一...
    開封第一講書人閱讀 52,156評(píng)論 1 308
  • 那天挟秤,我揣著相機(jī)與錄音,去河邊找鬼抄伍。 笑死艘刚,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的逝慧。 我是一名探鬼主播昔脯,決...
    沈念sama閱讀 40,743評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼笛臣!你這毒婦竟也來了云稚?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,659評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤沈堡,失蹤者是張志新(化名)和其女友劉穎静陈,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體诞丽,經(jīng)...
    沈念sama閱讀 46,200評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡鲸拥,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了僧免。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片刑赶。...
    茶點(diǎn)故事閱讀 40,424評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖懂衩,靈堂內(nèi)的尸體忽然破棺而出撞叨,到底是詐尸還是另有隱情金踪,我是刑警寧澤,帶...
    沈念sama閱讀 36,107評(píng)論 5 349
  • 正文 年R本政府宣布牵敷,位于F島的核電站胡岔,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏枷餐。R本人自食惡果不足惜靶瘸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望毛肋。 院中可真熱鬧怨咪,春花似錦、人聲如沸村生。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽趁桃。三九已至辽话,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間卫病,已是汗流浹背油啤。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評(píng)論 1 271
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蟀苛,地道東北人益咬。 一個(gè)月前我還...
    沈念sama閱讀 48,798評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像帜平,于是被迫代替她去往敵國(guó)和親幽告。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容