MapStruct系列(1)-MapStruct简介及入门案例

167 阅读3分钟

对象转换

在上篇文档中,参考阿里规范,在MVC三层架构中,涉及到了各种对象转换(比如DOT转VO),其中使用的是beanUtil工具类进行转换,但是这种转换存在问题,只能转换相同的字段,所有在实际开发中,有很多这种转换框架解决这类问题,这种框架一般都叫做Bean映射框架。

与动态映射框架相比,MapStruct 具有以下优势:

  • 通过使用普通方法调用而不是反射来快速执行

  • 编译时类型安全:只能映射相互映射的对象和属性,不能将订单实体意外映射到客户 DTO 等。

  • 在构建时清除错误报告,如果映射不完整(并非所有目标属性都被映射),映射不正确(找不到合适的映射方法或类型转换)。

常用的映射框架:

框架或工具类说明
commons包BeanUtils
spring beans下BeanUtils
Dozerdozer.sourceforge.net/documentati…
Orikaorika-mapper.github.io/orika-docs/
MapStructmapstruct.org/
ModelMappermodelmapper.org/
JMappergithub.com/jmapper-fra…

在这里插入图片描述

MapStruct 简介

官网地址 MapStruct 是一个代码生成器,它基于约定优于配置方法极大地简化了 Java bean 类型之间映射的实现。生成的映射代码使用简单的方法调用,因此速度快、类型安全且易于理解。

多层应用程序通常需要在不同的对象模型(例如实体和 DTO)之间进行映射。编写这样的映射代码是一项乏味且容易出错的任务。MapStruct 旨在通过尽可能自动化来简化这项工作。

与其他映射框架相比,MapStruct 在编译时生成 bean 映射,以确保高性能,允许快速的开发人员反馈和彻底的错误检查。

MapStruct 是一个注解处理器,基于JSR269(和lombok一样),它插入到 Java 编译器中,可用于命令行构建(Maven、Gradle 等)以及您首选的 IDE。

入门案例

需求:将Car转换为CarDTO,它们之间的字段并不是完全对应关系(字段名不同);

  1. 创建一个spring boot基础工程,引入pom,注意lombok必须放在mapstruct之前。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.13.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>org.pearl</groupId>
    <artifactId>spring-boot-validation-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-boot-validation-demo</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
        <org.mapstruct.version>1.4.2.Final</org.mapstruct.version>
    </properties>
    <dependencies>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.20</version>
        </dependency>
        <!--mapstruct所需的注释,例如@Mapping-->
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct</artifactId>
            <version>${org.mapstruct.version}</version>
        </dependency>
        <!--mapstruct生成映射器的注解处理器-->
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-processor</artifactId>
            <version>${org.mapstruct.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <!--排除只用于代码生成的jar-->
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                        <exclude>
                            <groupId>org.mapstruct</groupId>
                            <artifactId>mapstruct-processor</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

  1. 新建Car及CarDTO
@Data
public class Car {
    private String make;
    private int numberOfSeats;
    private String type;
}
@Data
public class CarDto {
    private String make;
    private int seatCount;
    private String type;
}
  1. 添加一个转换器,定义转换关系。
// 该@Mapper注解1 将接口标记为映射接口,并让 MapStruct 处理器在编译期间启动。
// 在一个接口中可以有多个映射方法,MapStruct 将为所有这些方法生成一个实现。
@Mapper
public interface CarMapper {

    // 接口声明一个成员实例,为客户端提供对映射器实现的访问
    CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);

    /**
     * 实际映射方法期望源对象作为参数并返回目标对象。它的名字可以自由选择。
     *
     * @param car 需要转换的对象
     * @return CarDto 转换后的对象
     */
    // @Mapping 标注隐射关系
    @Mapping(source = "numberOfSeats", target = "seatCount")
    CarDto carToCarDto(Car car);
}
  1. 测试,可以发现名字不同的属性也进行了转换
public class Test {
    public static void main(String[] args) {
        Car car=new Car();
        car.setMake("make");
        car.setNumberOfSeats(100);
        car.setType("type");

        CarDto carDto = CarMapper.INSTANCE.car2CarDto(car);
        System.out.println(carDto);
    }
}

在这里插入图片描述

  1. 查看编译后的代码,可以看出MapStruct根据转换器接口自动生成了转换代码实现类,首先创建转换目标对象,然后使用set方法进行赋值操作。 在这里插入图片描述