mapstruct

MapStruct 是一个属性映射工具,只需要定义一个 Mapper 接口,MapStruct 就会自动实现这个映射接口,避免了复杂繁琐的映射实现。MapStruct官网地址: http://mapstruct.org/

MapStruct 使用APT生成映射代码,其在效率上比使用反射做映射的框架要快很多。

mapstruct spring

MapStruct 结合spring使用,设定componentModel = "spring"即可,如下Mapper接口:

@Mapper(componentModel = "spring")
public interface CarDtoMapper{
Car dtoToEntity(CarDto dto);
}

生成的映射代码如下,发现实现类上添加了@Component注解


@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2021-04-26T11:02:50+0800",
comments = "version: 1.4.2.Final, compiler: IncrementalProcessingEnvironment from gradle-language-java-6.8.jar, environment: Java 1.8.0_211 (Oracle Corporation)"
)
@Component
public class CarDtoMapperImpl implements CarDtoMapper { @Override
public Car dtoToEntity(CarDto dto) {
if ( dto == null ) {
return null;
} Car car = new Car(); car.setName( dto.getName() ); return car;
}
}

mapstruct spring 使用的缺点

mapstruct结合spring,在使用方式上主要是需要编写接口文件和定义函数所带来编码工作量:

  1. 需要创建mapper接口文件,这个是mapstruct框架的必须要经历的过程,代码量增加
  2. Dto和Entity之间互相转换,需要在接口中添加一个方法,并且添加上InheritInverseConfiguration注解,如下
@InheritInverseConfiguration(name = "dtoToEntity")
CarDto entityToDto(Car dto);
  1. service 中依赖多个mapper转换,增加构造函数注入个数
  2. 覆盖已有对象,需要添加如下map方法,如下
Car dtoMapToEntity(CarDto dto, @MappingTarget Car car)

反向映射,同样需要添加如下方法

CarDto entityMapToDto(Car dto, @MappingTarget CarDto car);

理想的映射工具

对于对象映射,有一种理想的使用方式,伪代码如下

Car car = mapper.map(dto, Car.class);
// or
Car car = new Car();
mapper.map(dto, car); // 反向映射
CarDto dto = mapper.map(entity, CarDto.class);
// or
CarDto dto = new CarDto();
mapper.map(entity, dto);

只使用mapper对象,就可以解决任何对象之间的映射。

mapstruct 官方解决方案: mapstruct-spring-extensions

官方地址如下: https://github.com/mapstruct/mapstruct-spring-extensions

其思路是使用spring 的 Converter接口,官方用法如下



@Mapper(config = MapperSpringConfig.class)
public interface CarMapper extends Converter<Car, CarDto> {
@Mapping(target = "seats", source = "seatConfiguration")
CarDto convert(Car car);
} @ComponentScan("org.mapstruct.extensions.spring")
@Component
static class AdditionalBeanConfiguration {
@Bean
ConfigurableConversionService getConversionService() {
return new DefaultConversionService();
}
} @BeforeEach
void addMappersToConversionService() {
conversionService.addConverter(carMapper);
conversionService.addConverter(seatConfigurationMapper);
conversionService.addConverter(wheelMapper);
conversionService.addConverter(wheelsMapper);
conversionService.addConverter(wheelsDtoListMapper);
} @Test
void shouldKnowAllMappers() {
then(conversionService.canConvert(Car.class, CarDto.class)).isTrue();
then(conversionService.canConvert(SeatConfiguration.class, SeatConfigurationDto.class)).isTrue();
then(conversionService.canConvert(Wheel.class, WheelDto.class)).isTrue();
then(conversionService.canConvert(Wheels.class, List.class)).isTrue();
then(conversionService.canConvert(List.class, Wheels.class)).isTrue();
} @Test
void shouldMapAllAttributes() {
// Given
final Car car = new Car();
car.setMake(TEST_MAKE);
car.setType(TEST_CAR_TYPE);
final SeatConfiguration seatConfiguration = new SeatConfiguration();
seatConfiguration.setSeatMaterial(TEST_SEAT_MATERIAL);
seatConfiguration.setNumberOfSeats(TEST_NUMBER_OF_SEATS);
car.setSeatConfiguration(seatConfiguration);
final Wheels wheels = new Wheels();
final ArrayList<Wheel> wheelsList = new ArrayList<>();
final Wheel wheel = new Wheel();
wheel.setDiameter(TEST_DIAMETER);
wheel.setPosition(TEST_WHEEL_POSITION);
wheelsList.add(wheel);
wheels.setWheelsList(wheelsList);
car.setWheels(wheels); // When
final CarDto mappedCar = conversionService.convert(car, CarDto.class);
}

使用 mapstruct-spring-extensions,使用 ConfigurableConversionService, 虽然解决了使用同一个对象映射,但是代码量没有解决,同时,没有提供覆盖已有对象的使用方式

推荐 mapstruct-spring-plus

地址: https://github.com/ZhaoRd/mapstruct-spring-plus

这个项目参考了mapstruct-spring-extensions项目,同时使用APT技术,动态生成Mapper接口,解决编写接口的问题,提供IObejctMapper接口,提供所有的map方法。

maven引入

<properties>
<org.mapstruct.version>1.4.2.Final</org.mapstruct.version>
<io.github.zhaord.version>1.0.1.RELEASE</io.github.zhaord.version>
</properties>
...
<dependencies>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
<dependency>
<groupId>io.github.zhaord</groupId>
<artifactId>mapstruct-spring-plus-boot-starter</artifactId>
<version>${io.github.zhaord.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>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
<path>
<groupId>io.github.zhaord</groupId>
<artifactId>mapstruct-spring-plus-processor</artifactId>
<version>${io.github.zhaord.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>

gradle 引入

dependencies {
...
compile 'org.mapstruct:mapstruct:1.4.2.Final'
compile 'io.github.zhaord:mapstruct-spring-plus-boot-starter:1.0.1.RELEASE' annotationProcessor 'org.mapstruct:mapstruct-processor:1.4.2.Final'
testAnnotationProcessor 'org.mapstruct:mapstruct-processor:1.4.2.Final' // if you are using mapstruct in test code annotationProcessor 'io.github.zhaord:mapstruct-spring-plus-processor:1.0.1.RELEASE'
testAnnotationProcessor 'io.github.zhaord:mapstruct-spring-plus-processor:1.0.1.RELEASE' // if you are using mapstruct in test code
...
}

使用案例

使用代码如下


public enum CarType {
SPORTS, OTHER
} @Data
public class Car {
private String make;
private CarType type;
} @Data
@AutoMap(targetType = Car.class)
public class CarDto {
private String make; private String type; } @ExtendWith(SpringExtension.class)
@ContextConfiguration(
classes = {AutoMapTests.AutoMapTestConfiguration.class})
public class AutoMapTests { @Autowired
private IObjectMapper mapper; @Test
public void testDtoToEntity() { var dto = new CarDto();
dto.setMake("M1");
dto.setType("OTHER"); Car entity = mapper.map(dto, Car.class); assertThat(entity).isNotNull();
assertThat(entity.getMake()).isEqualTo("M1");
assertThat(entity.getCarType()).isEqualTo("OTHER"); } @ComponentScan("io.github.zhaord.mapstruct.plus")
@Configuration
@Component
static class AutoMapTestConfiguration { } }

AutoMap 生成的接口代码


@Mapper(
config = AutoMapSpringConfig.class,
uses = {}
)
public interface CarDtoToCarMapper extends BaseAutoMapper<CarDto, Car> {
@Override
@Mapping(
ignore = false
)
Car map(final CarDto source); @Override
@Mapping(
ignore = false
)
Car mapTarget(final CarDto source, @MappingTarget final Car target);
}

mapstruct-spring-plus 带来的便捷

  1. 使用AutoMap注解,减少了重复代码的编写,尤其是接口文件和映射方法
  2. 依赖注入,只需要注入IObjectMapper接口即可,具体实现细节和调用方法,对客户端友好
  3. 没有丢失mapstruct的功能和效率
  4. @Mapping注解,都可以使用@AutoMapField来完成字段的映射设置,因为@AutoMapField继承自@Mapping,比如字段名称不一致、跳过映射等

最新文章

  1. at 常用命令
  2. 一周学会go语言并应用 by王奇疏
  3. .NET yield
  4. JavaScript基础14——js的Math对象
  5. Zend Framework 2参考Zend\Authentication(Zend\Authentication介绍)
  6. Windows7 QT5.6.0(64位)使用mysql(64位)环境搭建详解
  7. cmake 学习笔记(一)
  8. python读取CSV文件
  9. 10款免费Bootstrap后台模板演示及下载
  10. codeforces 528D Fuzzy Search
  11. hihoCoder #1078 : 线段树的区间修改(线段树区间更新板子题)
  12. 使用NPOI-创建Excel
  13. Button 使用Command 按钮置灰未更新
  14. oracle数据库 查看被锁定表及解锁方法
  15. wamp 中安装cakephp Fatal error: You must enable the intl extension to use CakePHP. in XXX
  16. svg转png
  17. cacti的介绍、安装、配置、及维护
  18. GC.SuppressFinalize()的正确用法
  19. 在VSCode中成功安装Go相关插件问题:tools failed to install.
  20. nodejs 学习一 process.execPath 、 __dirname、process.cwd()的区别

热门文章

  1. mysql内一些可以报错注入的查询语句
  2. Linux系统(Centos7)最新版本Docker简易(yum)安装步骤
  3. Heron and His Triangle HDU - 6222
  4. P1115_最大子段和(JAVA语言)
  5. C# 8 - Nullable Reference Types 可空引用类型
  6. Linux内核源码分析之setup_arch (四)
  7. Java 并发工具类 CountDownLatch、CyclicBarrier、Semaphore、Exchanger
  8. Android学习之启动活动的最佳写法
  9. [DP]城市交通
  10. Nginx常用部分命令