简介
MapStruct 是一个代码生成器(可以生成对象映射转换的代码),它基于约定优于配置的方法,极大地简化了 Java bean 类型之间的映射实现。
生成的映射代码使用普通的方法调用,因此速度快、类型安全且易于理解。
适用场景
多层应用程序通常需要在不同的对象模型(例如实体和 DTO)之间进行映射。编写这样的映射代码是一项乏味且容易出错的任务。MapStruct 旨在通过尽可能地自动化来简化这项工作。
与其他映射框架相比,MapStruct 在编译时生成 bean 映射,这确保了高性能,允许快速的开发人员反馈和彻底的错误检查。
说白了就解决 beanCopy 的问题。
工作时机
MapStruct 是一个注解处理器,它插入到 Java 编译器中,可用于命令行构建(Maven、Gradle 等)以及您首选的 IDE。
MapStruct 使用合理的默认值,但在配置或实现特殊行为时会采取措施。
使用案例
1、添加依赖
// mapstruct implementation 'org.mapstruct:mapstruct:1.4.2.Final' annotationProcessor 'org.mapstruct:mapstruct-processor:1.4.2.Final'
2、定义两个类,一个 Car 类(JPA 实体类),和一个 CarDto 类(数据传输类)
这两个类型非常相似,只是在计数属性的字段名不同,并且 type 属性是 CarType 枚举类型,CarDto 中是字符串类型。
Car 类
public class Car { private String make; private int numberOfSeats; private CarType type; //constructor, getters, setters etc. }
CarType 类定义
public enum CarType { SEDAN; }
CarDto 类
public class CarDto { private String make; private int seatCount; private String type; //constructor, getters, setters etc. }
2、创建转换器类接口,定义需要将 Car 对象转换为 CarDto 对象
@Mapper public interface CarMapper { CarMapper INSTANCE = Mappers.getMapper( CarMapper.class ); @Mapping(source = "numberOfSeats", target = "seatCount") CarDto carToCarDto(Car car); }
1). @Mapper 注解将该接口标记为映射接口,并且让 MapStruct 处理器在编译期间进行处理,实际映射方法为 carToCarDto 目的就是原参数Car
,转换为方法返回结果对象 CarDto
2). 对于源对象和目标对象中有不同的属性名称,我们可以使用 @Mapping
注解进行映射。在默认情况下,将源对象和目标对象据欧不同的类型属性执行类型转换,例如,type
数据原来是枚举,可以自动转换为字符串。
3). 以一个 Mapper 接口也支持多个映射方法。所有的这些方法都由 MapStruct 框架来实现。我们可以从 Mappers
类中检索接口实现的实例。通常我们在接口中申明一个 INSTANCE
属性,提供对外访问的实例。
3、单元测试
@Test public void shouldMapCarToDto () { //given Car car = new Car("Morris", 5, CarType.SEDAN); //when CarDto carDto = CarMapper.INSTANCE.carToCarDto(car); //then assertThat(carDto).isNotNull(); assertThat(carDto.getMake()).isEqualTo("Morris"); assertThat(carDto.getSeatCount()).isEqualTo(5); assertThat(carDto.getType()).isEqualTo("SEDAN"); }
我们使用起来就是非常简单,运行结果如下:
核心总结
MapStruct
其实和 Lombok 有些类似,也是通过插件的方式在我们编译期为我们生成一些辅助的代码。但是MapStruct 专注是解决 BeanCopy 以及对象转换的场景。
下面我们看看,就是我们 CarMapper 的实现类 CarMapperImpl , 这些都是框架在编译期间,我们生成的代码。
如果我们使用 BeanUtils.copyProperties
来做对象的转换是需要通过反射来获取对象的属性,方法等信息。然后再去赋值。反射虽然能够解决一些通用性的场景,但是对于频繁的 List 复制,性能是不高的。我们可以看看下面copyProperties
的部分代码截图:
个人觉得MapStruct
的优势还是非常明显的,就是帮助我们少写了非常多的 getters, setters 进行对象的相互转换,性能高于任何一种属性复制。缺点就会降低程序编译速度,增加转换层提升代码复杂度。