Bean 自動(dòng)轉(zhuǎn)換神器 Mapstruct 官方的 Spring 插件真香啊!
《Java 面試指北》來(lái)啦!這是一份教你如何更高效地準(zhǔn)備面試的小冊(cè),涵蓋常見(jiàn)八股文(系統(tǒng)設(shè)計(jì)、常見(jiàn)框架、分布式、高并發(fā) ......)、優(yōu)質(zhì)面經(jīng)等內(nèi)容。
在幾年前,我就安利過(guò)Mapstruct這個(gè)神器,它可以代替BeanUtil來(lái)進(jìn)行DTO、VO、PO之間的轉(zhuǎn)換。它使用的是Java編譯期的 ?annotation processor 機(jī)制,說(shuō)白了它就是一個(gè)代碼生成器,代替你手工進(jìn)行類(lèi)型轉(zhuǎn)換期間的取值賦值操作。

很多開(kāi)源項(xiàng)目也是用的?Mapstruct 進(jìn)行?DTO、VO、PO?等對(duì)象的互轉(zhuǎn),就比如我在幾年分享的??eladmin 這個(gè)開(kāi)源項(xiàng)目:一份熱乎的 SpringBoot 前后端分離后臺(tái)管理系統(tǒng)分析!分模塊開(kāi)發(fā)、RBAC權(quán)限控制...?。
下面是 Guide 自己記錄的一些筆記:

我在很多項(xiàng)目都使用了它,代碼清爽得很,增加了大量摸魚(yú)時(shí)間,用過(guò)的都說(shuō)好。
@Mapper(componentModel?=?"spring")
public?interface?AreaMapping?{
????List?toVos(List?areas) ;
}
就這么幾行就把一個(gè)PO的集合轉(zhuǎn)換成了對(duì)應(yīng)VO的集合。
//?spring?bean?
@Autowired
AreaMapping?areaMapping
????
//?轉(zhuǎn)換源?areas????
List?areas?=?……;
//?轉(zhuǎn)換目標(biāo)?vos?
List?vos?=?areaMapping.toVos(areas)
換成你手寫(xiě)試試,起碼得五分之一炷香的功夫。
但是這樣寫(xiě)還是不太爽,每次都要掛對(duì)應(yīng)的Mapper類(lèi)。
Converter
Spring framework提供了一個(gè)Converter接口:
@FunctionalInterface
public?interface?Converter<S,?T>?{
????@Nullable
????T?convert(S?source);
????default??Converter?andThen(Converter?super?T,???extends?U>?after)?{
????????Assert.notNull(after,?"After?Converter?must?not?be?null");
????????return?(s)?->?{
????????????T?initialResult?=?this.convert(s);
????????????return?initialResult?!=?null???after.convert(initialResult)?:?null;
????????};
????}
}
它的作用是將S轉(zhuǎn)換為T,這和Mapstruct的作用不謀而合。
Converter會(huì)通過(guò)ConverterRegistry這個(gè)注冊(cè)接口注冊(cè)到ConversionService,然后你就可以通過(guò)ConversionService的convert方法來(lái)進(jìn)行轉(zhuǎn)換:
?????T?convert(@Nullable?Object?source,?Class?targetType) ;
MapStruct Spring Extensions
根據(jù)上面的機(jī)制官方推出了MapStruct Spring Extensions插件, 它實(shí)現(xiàn)了一種機(jī)制,所有的Mapstruct映射接口(Mapper)只要實(shí)現(xiàn)了Converter,都會(huì)自動(dòng)注冊(cè)到ConversionService,我們只需要通過(guò)ConversionService就能完成任何轉(zhuǎn)換操作。
/**
?*?@author?felord.cn
?*?@since?1.0.0
?*/
@Mapper(componentModel?=?"spring")
public?interface?CarMapper?extends?Converter<Car,?CarDto>?{
????@Mapping(target?=?"seats",?source?=?"seatConfiguration")
????CarDto?convert(Car?car);
}
調(diào)用時(shí):
@Autowired
private?ConversionService?conversionService;
Car?car?=?……;
CarDto?carDto?=?conversionService.convert(car,CarDto.class);
MapStruct Spring Extensions?會(huì)自動(dòng)生成一個(gè)適配類(lèi)處理Mapper注冊(cè):
package?org.mapstruct.extensions.spring.converter;
import?cn.felord.mapstruct.entity.Car;
import?cn.felord.mapstruct.entity.CarDto;
import?org.springframework.context.annotation.Lazy;
import?org.springframework.core.convert.ConversionService;
import?org.springframework.stereotype.Component;
/**
?*?@author?felord.cn
?*?@since?1.0.0
?*/
@Component
public?class?ConversionServiceAdapter?{
????private?final?ConversionService?conversionService;
????public?ConversionServiceAdapter(@Lazy?final?ConversionService?conversionService)?{
????????this.conversionService?=?conversionService;
????}
????public?CarDto?mapCarToCarDto(final?Car?source)?{
????????return?(CarDto)this.conversionService.convert(source,?CarDto.class);
????}
}
自定義
自定義適配類(lèi)的包路徑和名稱(chēng)
默認(rèn)情況下,生成的適配類(lèi)將位于包org.mapstruct.extensions.spring.converter中,名稱(chēng)固定為ConversionServiceAdapter。如果你希望修改包路徑或者名稱(chēng),你可以這樣:
package?cn.felord.mapstruct.config;
import?org.mapstruct.MapperConfig;
import?org.mapstruct.extensions.spring.SpringMapperConfig;
/**
?*?@author?felord.cn
?*?@since?1.0.0
?*/
@MapperConfig(componentModel?=?"spring")
@SpringMapperConfig(conversionServiceAdapterPackage?=?"cn.felord.mapstruct.config",
????????conversionServiceAdapterClassName?=?"MapStructConversionServiceAdapter")
public?class?MapperSpringConfig?{
}
?不指定
conversionServiceAdapterPackage元素,生成的 Adapter 類(lèi)將與注解的 Config 駐留在同一個(gè)包中,所以上面的路徑是可以省略的。
指定ConversionService
如果你的Spring IoC容器中有多個(gè)ConversionService,你可以通過(guò)@SpringMapperConfig注解的conversionServiceBeanName?參數(shù)指定。
package?cn.felord.mapstruct.config;
import?org.mapstruct.MapperConfig;
import?org.mapstruct.extensions.spring.SpringMapperConfig;
/**
?*?@author?felord.cn
?*?@since?1.0.0
?*/
@MapperConfig(componentModel?=?"spring")
@SpringMapperConfig(conversionServiceAdapterPackage?=?"cn.felord.mapstruct.config",
????????conversionServiceAdapterClassName?=?"MapStructConversionServiceAdapter",
???????????????????conversionServiceBeanName?=?"myConversionService")
public?class?MapperSpringConfig?{
}
集成Spring的內(nèi)置轉(zhuǎn)換
Spring內(nèi)部提供了很多好用的Converter實(shí)現(xiàn),有的并不直接開(kāi)放,如果你想用Mapstruct的機(jī)制使用它們,可以通過(guò)@SpringMapperConfig注解的?externalConversions注冊(cè)它們。
@MapperConfig(componentModel?=?"spring")
@SpringMapperConfig(
???externalConversions?=?@ExternalConversion(sourceType?=?String.class,?targetType?=?Locale.class))
public?interface?MapstructConfig?{}
會(huì)在適配器中自動(dòng)生成相應(yīng)的轉(zhuǎn)換:
@Component
public?class?ConversionServiceAdapter?{
??private?final?ConversionService?conversionService;
??public?ConversionServiceAdapter(@Lazy?final?ConversionService?conversionService)?{
????this.conversionService?=?conversionService;
??}
??public?Locale?mapStringToLocale(final?String?source)?{
????return?conversionService.convert(source,?Locale.class);
??}
}
總結(jié)
mapstruct-spring-annotations?使開(kāi)發(fā)人員能夠通過(guò)ConversionService使用定義的?Mapstruct?映射器,而不必單獨(dú)導(dǎo)入每個(gè)?Mapper,從而允許?Mapper?之間的松散耦合。,它本身不會(huì)影響Mapstruct的機(jī)制。
項(xiàng)目源碼地址:https://gitee.com/felord/mapstruct-spring-extensions
··········? END? ··············
近期文章精選?:
被惡心到了 差點(diǎn)被“破解”了! 輕量!Google 開(kāi)源了一個(gè)簡(jiǎn)易版 Spring ! 八股文又又又更新了! 2022 金蝶 Java 四面面經(jīng)(已OC) 一款跨時(shí)代的高性能 Java 框架!啟動(dòng)速度快到飛起
如果本文對(duì)你有幫助的話,歡迎點(diǎn)贊&在看&分享,這對(duì)我繼續(xù)分享&創(chuàng)作優(yōu)質(zhì)文章非常重要。感謝????
