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,比如字段名称不一致、跳过映射等

简化mapstruct代码: mapstruct-spring-plus的更多相关文章

  1. lombok 简化java代码注解

    lombok 简化java代码注解 安装lombok插件 以intellij ide为例 File-->Setting-->Plugins-->搜索"lombok plug ...

  2. Lombok简化Java代码

    导包:import lombok.Data; Lombok简化Java代码: 在Lombok中,生成构造方法的annotation一共有三个:@NoArgsConstructor, @Required ...

  3. 基于MVC4+EasyUI的Web开发框架经验总结(11)--使用Bundles处理简化页面代码

    在Web开发的时候,我们很多时候,需要引用很多CSS文件.JS文件,随着使用更多的插件或者独立样式文件,可能我们的Web界面代码会越来越臃肿,看起来也很累赘,在MVC里面提供了一个Bundle的对象, ...

  4. 使用匿名委托,Lambda简化多线程代码

    使用匿名委托,Lambda简化多线程代码   .net中的线程也接触不少了.在多线程中最常见的应用莫过于有一个耗时的操作需要放到线程中去操作,而在这个线程中我们需要更新UI,这个时候就要创建一个委托了 ...

  5. 基于纯Java代码的Spring容器和Web容器零配置的思考和实现(3) - 使用配置

    经过<基于纯Java代码的Spring容器和Web容器零配置的思考和实现(1) - 数据源与事务管理>和<基于纯Java代码的Spring容器和Web容器零配置的思考和实现(2) - ...

  6. 2018-08-20 中文代码之Spring Boot集成H2内存数据库

    续前文: 中文代码之Spring Boot添加基本日志, 源码库地址相同. 鉴于此项目中的数据总量不大(即使万条词条也在1MB之内), 当前选择轻量级而且配置简单易于部署的H2内存数据库比较合理. 此 ...

  7. Async/Await是这样简化JavaScript代码的

    译者按: 在Async/Await替代Promise的6个理由中,我们比较了两种不同的异步编程方法:Async/Await和Promise,这篇博客将通过示例代码介绍Async/Await是如何简化J ...

  8. 2018-08-24 中文代码之Spring Boot对H2数据库简单查询

    续前文: 中文代码之Spring Boot集成H2内存数据库 在词条中添加英文术语域: @Entity public class 词条 { @Id private long id; private S ...

  9. 2018-08-16 中文代码之Spring Boot添加基本日志

    之前中文代码之Spring Boot实现简单REST服务的演示服务不知为何中止. 新开issue: 演示服务中止 · Issue #2 · program-in-chinese/programming ...

随机推荐

  1. Hibernate在oracle中ID增长的方式

    引用链接:http://blog.csdn.net/w183705952/article/details/7367272 Hibernate在oracle中ID增长的方式 第一种:设置ID的增长策略是 ...

  2. innerHTML,innerText

    文本替换 <p id="p1">Hello World!</p><div>神经</div><h3 class="hh ...

  3. Line-line Intersection Gym - 102220C

    题目链接:https://vjudge.net/problem/Gym-102220C 题意:求n 条直线两两相交有几对(也可以重合). 思路:用map和pair存所有直线的斜率和与X轴的交点,假设与 ...

  4. 10、MyBatis教程之一对多处理

    11.一对多处理 一对多的理解: 一个老师拥有多个学生 如果对于老师这边,就是一个一对多的现象,即从一个老师下面拥有一群学生(集合)! 1.实体类编写 @Data public class Stude ...

  5. SSM 电影后台管理项目

    SSM 电影后台管理项目 概述 通过对数据库中一张表的CRUD,将相应的操作结果渲染到页面上. 笔者通过这篇博客还原了项目(当然有一些隐藏的坑),然后将该项目上传到了Github.Gitee,在末尾会 ...

  6. SQL 存储过程里调用另一个存储过程

    由于创建了一个存储过程,并且要在另一个存储过程里调用这个存储过程所以在网上找了一下相关的代码,现在总结一下,防止以后还会用到 由于这次我写的存储过程只需要返回一个求和的结果,所以我使用了output ...

  7. 普通 Javaweb项目模板的搭建

    普通 Javaweb项目模板的搭建 1. 创建一个web项目模板的maven项目 2.配置 Tomcat 服务器 3.先测试一下该空项目 4.注入 maven 依赖 5.创建项目的包结构 6.编写基础 ...

  8. kong更改日志格式

    基于业务的需求,需要对网关层的日志进行适当定制,以满足使用kibana的制图. 对于kong的日志格式更改,可查看到的资料都过于繁琐,特此记录. 修改kong的日志格式 # ctl edit depl ...

  9. kubernetes中有状态应用的优雅缩容

    将有状态的应用程序部署到Kubernetes是棘手的. StatefulSet使它变得容易得多,但是它们仍然不能解决所有问题.最大的挑战之一是如何缩小StatefulSet而不将数据留在断开连接的Pe ...

  10. [Fundamental of Power Electronics]-PART II-9. 控制器设计-9.2 负反馈对网络传递函数的影响

    9.2 负反馈对网络传递函数的影响 我们已经知道了如何推导开关变换器的交流小信号传递函数.例如,buck变换器的等效电路模型可以表示为图9.3所示.这个等效电路包含三个独立输入:控制输入变量\(\ha ...