对象转换
在上篇文档中,参考阿里规范,在MVC三层架构中,涉及到了各种对象转换(比如DOT转VO),其中使用的是beanUtil工具类进行转换,但是这种转换存在问题,只能转换相同的字段,所有在实际开发中,有很多这种转换框架解决这类问题,这种框架一般都叫做Bean映射框架。
与动态映射框架相比,MapStruct 具有以下优势:
-
通过使用普通方法调用而不是反射来快速执行
-
编译时类型安全:只能映射相互映射的对象和属性,不能将订单实体意外映射到客户 DTO 等。
-
在构建时清除错误报告,如果映射不完整(并非所有目标属性都被映射),映射不正确(找不到合适的映射方法或类型转换)。
常用的映射框架:
| 框架或工具类 | 说明 |
|---|---|
| commons包BeanUtils | |
| spring beans下BeanUtils | |
| Dozer | dozer.sourceforge.net/documentati… |
| Orika | orika-mapper.github.io/orika-docs/ |
| MapStruct | mapstruct.org/ |
| ModelMapper | modelmapper.org/ |
| JMapper | github.com/jmapper-fra… |
MapStruct 简介
官网地址 MapStruct 是一个代码生成器,它基于约定优于配置方法极大地简化了 Java bean 类型之间映射的实现。生成的映射代码使用简单的方法调用,因此速度快、类型安全且易于理解。
多层应用程序通常需要在不同的对象模型(例如实体和 DTO)之间进行映射。编写这样的映射代码是一项乏味且容易出错的任务。MapStruct 旨在通过尽可能自动化来简化这项工作。
与其他映射框架相比,MapStruct 在编译时生成 bean 映射,以确保高性能,允许快速的开发人员反馈和彻底的错误检查。
MapStruct 是一个注解处理器,基于JSR269(和lombok一样),它插入到 Java 编译器中,可用于命令行构建(Maven、Gradle 等)以及您首选的 IDE。
入门案例
需求:将Car转换为CarDTO,它们之间的字段并不是完全对应关系(字段名不同);
- 创建一个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>
- 新建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;
}
- 添加一个转换器,定义转换关系。
// 该@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);
}
- 测试,可以发现名字不同的属性也进行了转换
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);
}
}
- 查看编译后的代码,可以看出MapStruct根据转换器接口自动生成了转换代码实现类,首先创建转换目标对象,然后使用set方法进行赋值操作。