MapStruct 解决 Bean 属性拷贝性能问题

简介: 无意间看到项目中有小伙伴用到了 MapStruct 来做对象映射转换当时我就很好奇,这个是什么框架,能够解决什么问题,带着这两个疑问就有了下面的文章。

简介



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");
}


我们使用起来就是非常简单,运行结果如下:


image.png


核心总结


MapStruct其实和 Lombok 有些类似,也是通过插件的方式在我们编译期为我们生成一些辅助的代码。但是MapStruct 专注是解决 BeanCopy 以及对象转换的场景。


下面我们看看,就是我们 CarMapper 的实现类 CarMapperImpl , 这些都是框架在编译期间,我们生成的代码。


image.png


如果我们使用 BeanUtils.copyProperties来做对象的转换是需要通过反射来获取对象的属性,方法等信息。然后再去赋值。反射虽然能够解决一些通用性的场景,但是对于频繁的 List 复制,性能是不高的。我们可以看看下面copyProperties的部分代码截图:


image.png


个人觉得MapStruct的优势还是非常明显的,就是帮助我们少写了非常多的 getters, setters 进行对象的相互转换,性能高于任何一种属性复制。缺点就会降低程序编译速度,增加转换层提升代码复杂度。


参考文档





相关文章
MapStruct - 生成空对象解决方案
MapStruct - 生成空对象解决方案
1391 0
|
4月前
|
Java Spring 容器
循环依赖问题之实例化Bean是通过如何实现的
循环依赖问题之实例化Bean是通过如何实现的
|
4月前
|
安全 Java Apache
使用BeanUtils进行属性拷贝
使用BeanUtils进行属性拷贝
|
6月前
|
存储 XML Java
Spring框架学习 -- 读取和存储Bean对象
Spring框架学习 -- 读取和存储Bean对象
52 0
Spring框架学习 -- 读取和存储Bean对象
|
6月前
|
缓存 安全 Java
你还在用 BeanUtils?试试 MapStruct,优雅的对象转换解决方案
你还在用 BeanUtils?试试 MapStruct,优雅的对象转换解决方案
93 0
|
6月前
|
XML Java 数据格式
spring-bean配置信息重用(继承)和bean创建顺序是什么以及bean 对象的单例和多例讲解
spring-bean配置信息重用(继承)和bean创建顺序是什么以及bean 对象的单例和多例讲解
71 0
|
6月前
|
XML Java 数据格式
spring怎么去引用/注入集合/数组类型和 怎么通过 util 名称空间创建 list以及 怎么去通过级联属性赋值
spring怎么去引用/注入集合/数组类型和 怎么通过 util 名称空间创建 list以及 怎么去通过级联属性赋值
69 0
|
XML 存储 安全
Java源码类 - Properties类及多种读取方式
Java源码类 - Properties类及多种读取方式
173 0
|
存储 Java Spring
简单存 Bean 对象 -- 五大类注解以及 Bean 方法(上)
简单存 Bean 对象 -- 五大类注解以及 Bean 方法(上)
|
Java C++ Spring
Spring更简单的实现Bean对象的存取(利用注解储存和注入Bean对象)(下)
Spring更简单的实现Bean对象的存取(利用注解储存和注入Bean对象)(下)
122 0