還在使用BeanUtils.copyProperties()?來看看效率更高的MapStruct吧

最近在做性能優(yōu)化乡革,發(fā)現(xiàn)之前一個后臺項目的接口有一些慢寇僧,仔細(xì)看看發(fā)現(xiàn)代碼中有拷貝對象的邏輯,而且拷貝對象用到的還是BeanUtils.copyProperties()署拟。在阿里巴巴編碼規(guī)范插件中也是不支持使用這種方法進(jìn)行拷貝的婉宰,如果有使用這種方法,用編碼規(guī)約掃描會直接提示“避免用Apache Beanutils進(jìn)行屬性的copy”推穷。同時會說明:“說明:Apache BeanUtils性能較差心包,可以使用其他方案比如Spring BeanUtils, Cglib BeanCopier”。除了建議的這幾種方案外馒铃,還有一個更好的方案就是今天要介紹的MapsStruct蟹腾。

1.定義

什么是MapStruct?

MapStruct 是一個代碼生成器区宇,它基于約定優(yōu)于配置方法娃殖,極大地簡化了Java bean類型之間映射的實現(xiàn)。生成的映射代碼使用簡單的方法調(diào)用议谷,因此速度快炉爆、類型安全且易于理解。

2.實踐

2.1工程引入Maven依賴
<dependency>
  <groupId>org.mapstruct</groupId>
  <!-- jdk8以下就使用mapstruct -->
  <artifactId>mapstruct-jdk8</artifactId>
  <version>1.3.0.Final</version>
</dependency>

<plugin>
        <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.6.2</version>
    <configuration>
        <source>1.8</source>
        <target>1.8</target>
        <annotationProcessorPaths>
            <path>
             <groupId>org.mapstruct</groupId>
             <artifactId>mapstruct-processor</artifactId>
             <version>1.3.0.Final</version>
            </path>
        </annotationProcessorPaths>
    </configuration>
</plugin>
2.2定義實體類

定義2個實體類,一個是User芬首,一個是UserDTO赴捞,目標(biāo)是把User的值拷貝到UserDTO

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
/**
 * 用戶編號,主鍵
 */
private Long id;
/**
 * 用戶名
 */
private String name;
/**
 * 電話
 */
private Long mobile;
/**
 * 年齡
 */
private Integer age;
/**
 * 性別
 */
private Integer sex;
/**
 * 家庭住址
 */
private String address;
}

@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserDTO {
/**
 * 用戶編號郁稍,主鍵
 */
private Long id;
/**
 * 用戶名
 */
private String userName;
/**
 * 電話
 */
private Long mobile;
/**
 * 年齡
 */
private Integer age;
/**
 * 性別
 */
private Integer sex;
/**
 * 家庭住址
 */
private String address;
}

可以看到兩個實體的字段名稱不完全相同赦政,這種情況下,不能使用BeanUtil.copyProperties()耀怜。使用MapStruct就可以處理這種情況恢着,定義一個接口如下:
@Mapper
public interface UserConvert {
UserConvert INSTANCE = Mappers.getMapper(UserConvert.class);

/**
 * 轉(zhuǎn)換
 * @param user
 * @return
 */
@Mappings({
        @Mapping(source = "name", target = "userName"),
})
UserDTO convert(User user);

}
有拷貝對象的業(yè)務(wù)場景,直接使用如下代碼:

   UserDTO userDTO1 = UserConvert.INSTANCE.convert(user);

原理就是通過JSR 269注解處理器在target自動生成接口定義的實現(xiàn)類财破,具體如下:


image.png

3.性能

對常用的幾種方案做下性能上的對比掰派,這樣更能看出MapsStruts的效率,具體測試代碼如下:

User user =  new User(1L,"小王",15800001111L,25,1,"中國北京朝陽");
long timeTaken;
long time1 = System.currentTimeMillis();
UserDTO userDTO;
for (int i = 0;i<500000;i++){
    userDTO = new UserDTO();
    org.apache.commons.beanutils.BeanUtils.copyProperties(userDTO,user);
}
long time2 = System.currentTimeMillis();
timeTaken =time2-time1;

System.out.println("Apache Commons BeanUtils:"+timeTaken+"(ms)");

long time3 = System.currentTimeMillis();

for (int i = 0;i<500000;i++){
    userDTO = new UserDTO();
    BeanUtils.copyProperties(user,userDTO);
}
long time4 = System.currentTimeMillis();

timeTaken =time4-time3;
System.out.println("Spring BeanUtils:"+timeTaken+"(ms)");

long time5 = System.currentTimeMillis();
for (int i = 0;i<500000;i++){
    UserDTO userDTO1 = UserConvert.INSTANCE.convert(user);
}
long time6 = System.currentTimeMillis();
timeTaken =time6-time5;

System.out.println("MapStruct:"+timeTaken+"(ms)");

long time7 = System.currentTimeMillis();
for (int i = 0;i<500000;i++){
    UserDTO userDTO1 =copyField(user);
}
long time8 = System.currentTimeMillis();
timeTaken =time8-time7;

System.out.println("Getter/Setter:"+timeTaken+"(ms)");

執(zhí)行結(jié)果:

Apache Commons BeanUtils:5767(ms)
Spring BeanUtils:1102(ms)
MapStruct:14(ms)
Getter/Setter:14(ms)

看到上面的結(jié)果狈究,可以得出結(jié)論Apache Commons BeanUtils確實耗時時間長碗淌,效率低,用Spring BeanUtils效率至少可以提高4倍抖锥,用MapStruct效率可以提高40倍亿眠,甚至可以和自己Getter/Setter的效率不相上下(注:效率倍數(shù)只是本次測試的數(shù)據(jù),理論上轉(zhuǎn)換的字段越多磅废,循環(huán)次數(shù)越多纳像,效果越明顯)。

4.總結(jié)

本文主要講了MapStruct的使用方法以及與其它拷貝對象方法的性能比較拯勉,著重突出MapStruct處理效率竟趾。除了上述實踐中的簡單使用,MapStruct還有許多其它的使用方法宫峦,具體內(nèi)容可以看下它的官網(wǎng)(https://mapstruct.org)岔帽。
總的來說MapStruct是一個功能強大的創(chuàng)建映射器的工具庫,可以減少開發(fā)人員編寫一些基本模板代碼的工作量导绷,提高編碼效率犀勒。

參考資料:
1.https://mapstruct.org/

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市妥曲,隨后出現(xiàn)的幾起案子贾费,更是在濱河造成了極大的恐慌,老刑警劉巖檐盟,帶你破解...
    沈念sama閱讀 222,464評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件褂萧,死亡現(xiàn)場離奇詭異,居然都是意外死亡葵萎,警方通過查閱死者的電腦和手機导犹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,033評論 3 399
  • 文/潘曉璐 我一進(jìn)店門唱凯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人锡足,你說我怎么就攤上這事波丰。” “怎么了舶得?”我有些...
    開封第一講書人閱讀 169,078評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長爽蝴。 經(jīng)常有香客問我沐批,道長,這世上最難降的妖魔是什么蝎亚? 我笑而不...
    開封第一講書人閱讀 59,979評論 1 299
  • 正文 為了忘掉前任九孩,我火速辦了婚禮,結(jié)果婚禮上发框,老公的妹妹穿的比我還像新娘躺彬。我一直安慰自己,他們只是感情好梅惯,可當(dāng)我...
    茶點故事閱讀 69,001評論 6 398
  • 文/花漫 我一把揭開白布宪拥。 她就那樣靜靜地躺著,像睡著了一般铣减。 火紅的嫁衣襯著肌膚如雪她君。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,584評論 1 312
  • 那天葫哗,我揣著相機與錄音缔刹,去河邊找鬼。 笑死劣针,一個胖子當(dāng)著我的面吹牛校镐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播捺典,決...
    沈念sama閱讀 41,085評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼鸟廓,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了辣苏?” 一聲冷哼從身側(cè)響起肝箱,我...
    開封第一講書人閱讀 40,023評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎稀蟋,沒想到半個月后煌张,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,555評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡退客,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,626評論 3 342
  • 正文 我和宋清朗相戀三年骏融,在試婚紗的時候發(fā)現(xiàn)自己被綠了链嘀。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,769評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡档玻,死狀恐怖怀泊,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情误趴,我是刑警寧澤霹琼,帶...
    沈念sama閱讀 36,439評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站凉当,受9級特大地震影響枣申,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜看杭,卻給世界環(huán)境...
    茶點故事閱讀 42,115評論 3 335
  • 文/蒙蒙 一忠藤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧楼雹,春花似錦模孩、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,601評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至携悯,卻和暖如春祭芦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背憔鬼。 一陣腳步聲響...
    開封第一講書人閱讀 33,702評論 1 274
  • 我被黑心中介騙來泰國打工龟劲, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人轴或。 一個月前我還...
    沈念sama閱讀 49,191評論 3 378
  • 正文 我出身青樓昌跌,卻偏偏與公主長得像,于是被迫代替她去往敵國和親照雁。 傳聞我的和親對象是個殘疾皇子蚕愤,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,781評論 2 361

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