一、Object mapping 的技術(shù)分類:
- 運(yùn)行期 反射調(diào)用set/get 或者是直接對(duì)成員變量賦值 。 該方式通過(guò)invoke執(zhí)行賦值,實(shí)現(xiàn)時(shí)一般會(huì)采用beanutil, Javassist等開源庫(kù)。這類的代表:Dozer,ModelMaper
- 編譯期 動(dòng)態(tài)生成set/get代碼的class文件 ,在運(yùn)行時(shí)直接調(diào)用該class文件。該方式實(shí)際上扔會(huì)存在set/get代碼,只是不需要自己寫了。 這類的代表:MapStruct,Selma,Orika ### 主要框架性能對(duì)比: 每秒鐘執(zhí)行的object mapping越多越好。

明顯可以看出通過(guò)在運(yùn)行期進(jìn)行反射的方式執(zhí)行,性能遠(yuǎn)不如編譯器生成class的方式;
MapStruct 與 Selma的對(duì)比: https://java.libhunt.com/project/mapstruct/vs/selma
MapStruct 與 ModelMapper的對(duì)比: https://java.libhunt.com/project/mapstruct/vs/modelmapper
綜合比較性能、問(wèn)題排查、文檔、成熟度、擴(kuò)展性等因素來(lái)考慮,MapStruct 是一個(gè)不錯(cuò)的選擇;
二、Maven依賴與配置:
<dependencies>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId>
<version>1.2.0.CR1</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.2.0.CR1</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version> <!-- or newer version -->
<configuration>
<source>1.8</source> <!-- depending on your project -->
<target>1.8</target> <!-- depending on your project -->
</configuration>
</plugin>
</plugins>
</build>
三、MapStruct的基本使用:
//DO 實(shí)體對(duì)象
public class Car {
private String make;
private int numberOfSeats;
private String type;
// get/set .....
}
//DTO 傳輸對(duì)象
public class CarDto {
private String make;
private int seatCount;
private String type;
// get/set......
}
@Mapper
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper( CarMapper.class );
/**
* The actual mapping method expects the source object as parameter and returns the target object.
* Its name can be freely chosen.
* 入?yún)ⅲ涸磳?duì)象
* 返回:轉(zhuǎn)換目標(biāo)對(duì)象
* 方法名不限制
*/
@Mapping(source = "numberOfSeats", target = "seatCount")
CarDto carToCarDto(Car car);
}
// 測(cè)試類
public class MapStructDemo {
public static void main(String[] args) {
//given
Car car = new Car( "Morris", 5, "SENAD" );
//when
CarDto carDto = CarMapper.INSTANCE.carToCarDto( car );
System.out.println(carDto);
}
}
編譯后生成的部分代碼結(jié)構(gòu):
public class CarMapperImpl implements CarMapper {
public CarMapperImpl() {
}
public CarDto carToCarDto(Car car) {
if (car == null) {
return null;
} else {
CarDto carDto = new CarDto();
carDto.setSeatCount(car.getNumberOfSeats());
carDto.setMake(car.getMake());
carDto.setType(car.getType());
return carDto;
}
}
}
可以看出MapStruct還需要依賴對(duì)象的get/set方法,有時(shí)候編寫一堆的get/set方法看上去很不美觀,期望能通過(guò)自動(dòng)生成的方式插入get/set方法,其解決方案是使用lombok。mapstrcut官網(wǎng)也有二者結(jié)合的例子: https://github.com/mapstruct/mapstruct/issues/510 Lombok帶來(lái)的問(wèn)題是,如果我們期望通過(guò)公有的get/set方法范圍私有屬性時(shí),IDE會(huì)提示方法不存在,這個(gè)時(shí)候我們可以下載安裝Intellij Idea中的”Lombok plugin”來(lái)解決,但是這種方案帶來(lái)了一定的繁瑣性。比較好的方式是,對(duì)于DO或者DTO中的屬性,如果屬性為私有屬性,需要通過(guò)get/set方法來(lái)訪問(wèn)的,那么就手工生成get/set方法,如果屬性本身為共有屬性的,那么就可以借助Lombok來(lái)自動(dòng)生成get/set方法了。
四、MapStruct的屬性與方法:
1. @Mapper注解的componentModel屬性
componentModel 屬性用于指定自動(dòng)生成的接口實(shí)現(xiàn)類的組件類型。這個(gè)屬性支持四個(gè)值:
- default: 這是默認(rèn)的情況,mapstruct不使用任何組件類型, 可以通過(guò)Mappers.getMapper(Class)方式獲取自動(dòng)生成的實(shí)例對(duì)象。
- cdi: the generated mapper is an application-scoped CDI bean and can be retrieved via @Inject
- spring: 生成的實(shí)現(xiàn)類上面會(huì)自動(dòng)添加一個(gè)@Component注解,可以通過(guò)Spring的 @Autowired方式進(jìn)行注入
- jsr330: 生成的實(shí)現(xiàn)類上會(huì)添加@javax.inject.Named 和@Singleton注解,可以通過(guò) @Inject注解獲取。