性能爆棚的實(shí)體轉(zhuǎn)換復(fù)制工具M(jìn)apStruct使用詳解
引言
Java項(xiàng)目中實(shí)體轉(zhuǎn)換無處不在,當(dāng)實(shí)體字段較多或者大批量的進(jìn)行復(fù)制時(shí),通過手工setter/getter顯得太LOW,同時(shí)兼?zhèn)涓咝阅芤笄闆r下,MapStruct完全完全能夠勝任。官方解釋,MapStruct是一個(gè)代碼生成器,它基于約定優(yōu)于配置的方法,極大地簡(jiǎn)化了Java bean類型之間映射的實(shí)現(xiàn)。生成的映射代碼使用普通方法調(diào)用,因此快速、類型安全且易于理解。因?yàn)镸apStruct是在編譯期間生成setter/getter方法,實(shí)際運(yùn)行時(shí)就是直接調(diào)用setter/getter,效率會(huì)非常高。
優(yōu)點(diǎn)
- MapStruct編譯期生成映射代碼,所以可以在編譯時(shí)暴露映射錯(cuò)誤的代碼,讓錯(cuò)誤提前暴露;
- 因?yàn)槭褂胹etter/getter方式,而非反射方式,所以可以更快的執(zhí)行效率;
- 可以實(shí)現(xiàn)深拷貝,自動(dòng)類型轉(zhuǎn)換,如枚舉轉(zhuǎn)換;
- 進(jìn)行自定義的映射,多種映射方式,下邊具體說明;
性能對(duì)比
| 對(duì)比對(duì)象 | 10個(gè)對(duì)象復(fù)制1次 | 1萬個(gè)對(duì)象復(fù)制1次 | 100萬個(gè)對(duì)象復(fù)制1次 | 100萬個(gè)對(duì)象復(fù)制5次 |
|---|---|---|---|---|
| MapStruct | 0ms | 3ms | 96ms | 281ms |
| Hutools的BeanUtil | 23ms | 102ms | 1734ms | 8316ms |
| Spring的BeanUtils | 2ms | 47ms | 726ms | 3676ms |
| Apache的BeanUtils | 20ms | 156ms | 10658ms | 52355ms |
| Apache的PropertyUtils | 5ms | 68ms | 6767ms | 30694ms |
使用
依賴
<!-- MapStruct核心,包含了一些必要的注解-->
<dependencies>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<annotationProcessorPaths>
<!-- MapStruct編譯,注解處理器,根據(jù)注解自動(dòng)生成Mapper的實(shí)現(xiàn) -->
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
定義轉(zhuǎn)換接口
/**
* 測(cè)試接口
*
* @author reboot
*/
@Mapper
public interface OrderConvertor {
/**
* 實(shí)例
*/
OrderConvertor INSTANCE = Mappers.getMapper(OrderConvertor.class);
/**
* OrderDo -> OrderModel
*
* @param orderDo 訂單實(shí)體
* @return {@link OrderModel}
*/
OrderModel toModel(OrderDo orderDo);
/**
* OrderDo -> OrderModel
*
* @param orderDos 訂單實(shí)體
* @return {@link OrderModel}
*/
List<OrderModel> toModel(List<OrderDo> orderDos);
/**
* OrderModel -> OrderDo
*
* @param orderModel 訂單模型
* @return {@link OrderDo}
*/
OrderDo toDo(OrderModel orderModel);
/**
* OrderModel -> OrderDo
*
* @param orderModels 訂單模型
* @return {@link OrderDo}
*/
List<OrderDo> toDo(List<OrderModel> orderModels);
}
編譯結(jié)果

MapStruct會(huì)自動(dòng)生成對(duì)應(yīng)接口的實(shí)現(xiàn),并自動(dòng)完成屬性映射關(guān)系,List會(huì)自動(dòng)進(jìn)行批量處理。
調(diào)用
/**
* 訂單服務(wù)
*
* @author reboot
*/
@Service
public class OrderService {
/**
* 獲取訂單列表
*
* @return {@link List}<{@link OrderModel}>
*/
public List<OrderModel> getOrderList() {
// 獲取數(shù)據(jù)庫數(shù)據(jù)DO
List<OrderDo> result = selectOrderList();
// 參數(shù)轉(zhuǎn)換
return OrderConvertor.INSTANCE.toModel(result);
}
}
插件

上邊的使用方式雖然能夠正常使用,但是在一些屬性配置映射上和提示上,如果使用插件能夠提升使用體驗(yàn),IDEA中可以直接安裝Mapstruct Support插件,當(dāng)然Eclipse也有對(duì)應(yīng)的插件。
特性
- 突出顯示目標(biāo)屬性和源屬性。將目標(biāo)屬性和源屬性轉(zhuǎn)到聲明的setter / getter中;
- 錯(cuò)誤和快速修復(fù):
- 缺少@Mapper或@MapperConfig注解檢查;
- 快速修復(fù)未映射的目標(biāo)屬性,添加未映射目標(biāo)屬性和忽略未映射目標(biāo)屬性;

其他用法
更加詳細(xì)的內(nèi)容可以查看官方文檔,發(fā)布文章時(shí)最新版本是 MapStruct 1.5.3.Final.html。
基礎(chǔ)映射
@Mapper
public interface CarMapper {
@Mapping(target = "manufacturer", source = "make")
@Mapping(target = "seatCount", source = "numberOfSeats")
CarDto carToCarDto(Car car);
@Mapping(target = "fullName", source = "name")
PersonDto personToPersonDto(Person person);
}
target表示目標(biāo)屬性名,source表示源屬性名,一般在目標(biāo)屬性和源屬性不同時(shí)使用,相同的屬性名會(huì)自動(dòng)進(jìn)行映射。
映射器添加自定義方法
@Mapper
public interface CarMapper {
@Mapping(...)
...
CarDto carToCarDto(Car car);
default PersonDto personToPersonDto(Person person) {
//hand-written mapping logic
}
}
自定義方法personToPersonDto并實(shí)現(xiàn),在生成的實(shí)現(xiàn)類中會(huì)進(jìn)行覆蓋使用。
多個(gè)源參數(shù)映射
@Mapper
public interface AddressMapper {
@Mapping(target = "description", source = "person.description")
@Mapping(target = "houseNumber", source = "address.houseNo")
DeliveryAddressDto personAndAddressToDeliveryAddressDto(Person person, Address address);
@Mapping(target = "description", source = "person.description")
@Mapping(target = "houseNumber", source = "hn")
DeliveryAddressDto personAndAddressToDeliveryAddressDto(Person person, Integer hn);
}
存在多個(gè)源參數(shù),使用參數(shù)名.屬性名的方式進(jìn)行表示,也可以直接使用基礎(chǔ)類型的屬性名稱。
嵌套屬性映射到當(dāng)前目標(biāo)
@Mapper
public interface CustomerMapper {
@Mapping( target = "name", source = "record.name" )
@Mapping( target = ".", source = "record" )
@Mapping( target = ".", source = "account" )
Customer customerDtoToCustomer(CustomerDto customerDto);
}
當(dāng)源參數(shù)中存在對(duì)象屬性,可以手動(dòng)進(jìn)行映射,或者直接使用"."的方式將對(duì)象中的屬性全部映射到當(dāng)前目標(biāo)對(duì)象。
表達(dá)式方式
@Mapper
public interface SourceTargetMapper {
SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class );
@Mapping(
target = "timeAndFormat",
expression = "java( new org.sample.TimeAndFormat( s.getTime(), s.getFormat() ) )"
)
Target sourceToTarget(Source s);
}
支持使用java代碼塊進(jìn)行轉(zhuǎn)換,一般可以將靜態(tài)方法處理的字段放到這里。
更新現(xiàn)有實(shí)例
@Mapper
public interface CarMapper {
void updateCarFromDto(CarDto carDto, @MappingTarget Car car);
}
@MappingTarget源參數(shù),編譯時(shí)會(huì)將carDto參數(shù)中的屬性映射到car參數(shù)中。
Map映射
@Mapper
public interface CustomerMapper {
@Mapping(target = "name", source = "customerName")
Customer toCustomer(Map<String, String> map);
}
直接將map中的key進(jìn)行映射。
更多用法
還有更多其他用法,比如:
- 支持映射定義的public屬性;
- 支持映射參數(shù)Builder模式;
- 使用注入方式引入轉(zhuǎn)換器;
- 數(shù)據(jù)類型字段轉(zhuǎn)換,如枚舉、日期,支持日期格式化,支持?jǐn)?shù)字類型格式化,具體可以看 Implicit type conversions;
- 集合類型自動(dòng)轉(zhuǎn)換;
- 轉(zhuǎn)換Stream;
- ......
總結(jié)
MapStruct還有很多其他高階特性,限于篇幅文章僅僅列舉部分示例,有興趣的同學(xué)可以查看對(duì)應(yīng)文檔試試。使用適當(dāng)?shù)墓ぞ哂行岣呔幊绦?,在使用工具過程中我們也了解其實(shí)現(xiàn)原理,不斷提高自身。后邊有時(shí)間也把MapStruct實(shí)現(xiàn)原理拿出來講講,跟大家一起學(xué)習(xí)進(jìn)步!
以上就是性能爆棚的實(shí)體轉(zhuǎn)換復(fù)制工具M(jìn)apStruct使用詳解的詳細(xì)內(nèi)容,更多關(guān)于MapStruct實(shí)體轉(zhuǎn)換復(fù)制的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
IDEA創(chuàng)建springboot依賴下載很慢的解決方法
maven會(huì)使用遠(yuǎn)程倉庫來加載依賴,是一個(gè)國外的網(wǎng)站,所以會(huì)很慢,本文主要介紹了IDEA創(chuàng)建springboot依賴下載很慢的解決方法,具有一定的參考價(jià)值,感興趣的可以了解一下2023-12-12
Springboot中動(dòng)態(tài)語言groovy介紹
Apache的Groovy是Java平臺(tái)上設(shè)計(jì)的面向?qū)ο缶幊陶Z言,這門動(dòng)態(tài)語言擁有類似Python、Ruby和Smalltalk中的一些特性,可以作為Java平臺(tái)的腳本語言使用,這篇文章主要介紹了springboot中如何使用groovy,需要的朋友可以參考下2022-09-09
springboot中@ConfigurationProperties無效果的解決方法
本文主要介紹了springboot中@ConfigurationProperties無效果,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-06-06
java使用PDFRenderer實(shí)現(xiàn)預(yù)覽PDF功能
這篇文章主要為大家詳細(xì)介紹了java使用PDFRenderer實(shí)現(xiàn)預(yù)覽PDF功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-12-12
Java必備知識(shí)之位運(yùn)算及常見進(jìn)制解讀
從現(xiàn)代計(jì)算機(jī)中所有的數(shù)據(jù)二進(jìn)制的形式存儲(chǔ)在設(shè)備中。即 0、1 兩種狀態(tài),計(jì)算機(jī)對(duì)二進(jìn)制數(shù)據(jù)進(jìn)行的運(yùn)算(+、-、*、/)都是叫位運(yùn)算,即將符號(hào)位共同參與運(yùn)算的運(yùn)算2021-10-10
Spring Boot使用線程池創(chuàng)建多線程的完整示例
在 Spring Boot 2 中,可以使用 @Autowired 注入 線程池(ThreadPoolTaskExecutor 或 ExecutorService),從而管理線程的創(chuàng)建和執(zhí)行,以下是使用 @Autowired 方式注入線程池的完整示例,感興趣的朋友一起看看吧2025-03-03
Spring加載properties文件的兩種方式實(shí)例詳解
這篇文章主要介紹了Spring加載properties文件的兩種方式,需要的朋友可以參考下2018-02-02
javaweb實(shí)現(xiàn)百度GPS定位接口(經(jīng)緯度)
這篇文章主要介紹了javaweb實(shí)現(xiàn)百度GPS定位接口(經(jīng)緯度),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02

