最近在做性能優(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)類财破,具體如下:
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/