類型轉(zhuǎn)換神器Mapstruct新出的Spring插件真好用
胖哥在幾年前安利過Mapstruct這個(gè)神器,它可以代替BeanUtil來進(jìn)行DTO、VO、PO之間的轉(zhuǎn)換。它使用的是Java編譯期的 ?annotation processor 機(jī)制,說白了它就是一個(gè)代碼生成器,代替你手工進(jìn)行類型轉(zhuǎn)換期間的取值賦值操作。
胖哥很多項(xiàng)目都使用了它,代碼清爽得很,增加了大量摸魚時(shí)間,用過的都說好。
@Mapper(componentModel?=?"spring")
public?interface?AreaMapping?{
????List?toVos(List?areas) ;
}
就這么幾行就把一個(gè)PO的集合轉(zhuǎn)換成了對應(yīng)VO的集合。
//?spring?bean?
@Autowired
AreaMapping?areaMapping
????
//?轉(zhuǎn)換源?areas????
List?areas?=?……;
//?轉(zhuǎn)換目標(biāo)?vos?
List?vos?=?areaMapping.toVos(areas)
換成你手寫試試,起碼得五分之一炷香的功夫。
但是這樣寫還是不太爽,每次都要掛對應(yīng)的Mapper類。
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會通過ConverterRegistry這個(gè)注冊接口注冊到ConversionService,然后你就可以通過ConversionService的convert方法來進(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,都會自動(dòng)注冊到ConversionService,我們只需要通過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?會自動(dòng)生成一個(gè)適配類處理Mapper注冊:
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);
????}
}
自定義
自定義適配類的包路徑和名稱
默認(rèn)情況下,生成的適配類將位于包org.mapstruct.extensions.spring.converter中,名稱固定為ConversionServiceAdapter。如果你希望修改包路徑或者名稱,你可以這樣:
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 類將與注解的 Config 駐留在同一個(gè)包中,所以上面的路徑是可以省略的。
指定ConversionService
如果你的Spring IoC容器中有多個(gè)ConversionService,你可以通過@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),有的并不直接開放,如果你想用Mapstruct的機(jī)制使用它們,可以通過@SpringMapperConfig注解的?externalConversions注冊它們。
@MapperConfig(componentModel?=?"spring")
@SpringMapperConfig(
???externalConversions?=?@ExternalConversion(sourceType?=?String.class,?targetType?=?Locale.class))
public?interface?MapstructConfig?{}
會在適配器中自動(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?使開發(fā)人員能夠通過ConversionService使用定義的?Mapstruct?映射器,而不必單獨(dú)導(dǎo)入每個(gè)?Mapper,從而允許?Mapper?之間的松散耦合。,它本身不會影響Mapstruct的機(jī)制。相關(guān)的DEMO可以通過公眾號回復(fù) mapstructspring 獲取。
完
往期推薦

IntelliJ IDEA 卡成球了?

知網(wǎng)哭窮賠不起1200億,網(wǎng)友:收錢時(shí)咋不嫌多

用了Stream后,代碼反而越寫越丑?
有道無術(shù),術(shù)可成;有術(shù)無道,止于術(shù)
歡迎大家關(guān)注Java之道公眾號
好文章,我在看??
