前言 因项目的DTO越来越多,且不同层级的DTO和javabean需要转换,大量使用bean.copy方法,造成性能下降,阅读性差的问题,准备引入MapStruct进行javabean数据的映射转换。
MapStruct是什么 MapStruct是一个Java注释处理器,用于为Java bean类生成类型安全和高性能的映射器。它使您不必手工编写映射代码,这是一个繁琐且容易出错的任务。该生成器具有合理的默认值和许多内置的类型转换,但是在配置或实现特殊行为时,它会自动退出。
优点
通过使用普通方法调用而不是反射来快速执行
编译时类型安全。只能映射彼此映射的对象和属性,因此不会将实体意外映射到客户DTO等等。
独立代码-没有运行时依赖
在构建时清除错误报告,如: a. 映射不完整(并非所有目标属性都被映射) b. 映射不正确(找不到正确的映射方法或类型转换)
易于调试的映射代码(或可以手动编辑,例如,如果生成器中有错误)
运行环境 这里使用的是MapStruct 1.3.1版本,需要Java 1.8或以上(1.2.x版本好像不支持Java 1.8,需要其他包代替)
简单说明 需要在两种类型之间创建映射,请声明一个映射器类,如下所示:
1 2 3 4 5 6 @Mapper public interface CarMapper { CarMapper INSTANCE = Mappers.getMapper( CarMapper.class ) ; @Mapping (source = "numberOfSeats" , target = "seatCount" ) CarDto carToCarDto (Car car) ; }
在编译时,MapStruct将生成此接口的实现。生成的实现使用简单的Java方法调用在源对象和目标对象之间进行映射,即不涉及反射。默认情况下,如果属性在源和目标中具有相同的名称,则将映射它们,但是您可以使用@Mapping和少量其他注释来控制此属性和许多其他方面。
开发工具插件 IntelliJ idea 中可在插件市场中下载使用插件(https://plugins.jetbrains.com/plugin/10036-mapstruct-support ) PS:这里用到Lombok,因此也需要下载idea插件市场的一个Lombok插件
确保您idea版本至少为 2018.2.x(由于对maven-compiler-plugin的注解处理器的支持来自该版本,因此是必需的)。 在IntelliJ中启用注释处理(Build, Execution, Deployment -> Compiler -> Annotation Processors)
Maven环境配置 对于基于Maven的项目,将以下内容添加到您的POM文件中以使用MapStruct(依赖关系在Maven Central中可用):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 ...<properties > <org.mapstruct.version > 1.3.1.Final</org.mapstruct.version > </properties > ...<dependencies > <dependency > <groupId > org.mapstruct</groupId > <artifactId > mapstruct</artifactId > <version > ${org.mapstruct.version}</version > </dependency > </dependencies > ...<build > <plugins > <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-compiler-plugin</artifactId > <version > 3.8.0</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 > org.projectlombok</groupId > <artifactId > lombok</artifactId > <version > ${lombok.version}</version > </path > </annotationProcessorPaths > </configuration > </plugin > </plugins > </build > ...
碰到的问题 当MapStruct和Lombok混合使用时,会出现异常,找不到字段:Warning:java: 来自注释处理程序 'net.java.dev.hickory.prism.internal.PrismGenerator' 的受支持 source 版本 'RELEASE_6' 低于 -source '1.8'
Error:(101, 16) java: No property named "numberOfSeats" exists in source parameter(s). Did you mean "null"?
解决方法只需要在<maven-compiler-plugin>节点的<annotationProcessorPaths>中添加:
1 2 3 4 5 6 <path > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > <version > ${lombok.version}</version > </path >
下面是代码
需要
类
说明
源实体
Car
数据来源实体,数据库实体(this) 转换到 DTO
目标实体
CarDto
转换目标实体,DTO 转换到 响应实体(this)
映射interface
CarMapper
定义用于转换映射的interface,MapStruct会实现这个接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 package raven.spring.study;import lombok.AllArgsConstructor;import lombok.Data;import lombok.Getter;import lombok.ToString;import org.junit.jupiter.api.Test;import org.mapstruct.Mapper;import org.mapstruct.Mapping;import org.mapstruct.factory.Mappers;class MapStructTests { @Test public void shouldMapCarToDtoTest () { Car car = new Car( "Morris" , 5 , CarType.SEDAN ,200.19 ); CarDto carDto = CarMapper.INSTANCE.carToCarDto(car); System.out.println(car); System.out.println(carDto); System.out.println("同类型同名:" + carDto.getMake().equals("Morris" )); System.out.println("同类型非同名" + (carDto.getSeatCount() == (5 ))); System.out.println("字符串-枚举:" + carDto.getType().equals("SEDAN" )); System.out.println("非同类型非同名:" + carDto.getMaxCarSpeed().equals(String.valueOf(car.getMaxSpeed()))); } }@Data @AllArgsConstructor @ToString class Car { private String make; private int numberOfSeats; private CarType type; private double maxSpeed; }@Data @ToString class CarDto { private String make; private int seatCount; private String type; private String maxCarSpeed; }@AllArgsConstructor @Getter enum CarType { SEDAN("SEDAN" ); private String key; }@Mapper interface CarMapper { CarMapper INSTANCE = Mappers.getMapper( CarMapper.class ) ; @Mapping (source = "numberOfSeats" , target = "seatCount" ) @Mapping (source = "maxSpeed" , target = "maxCarSpeed" ) CarDto carToCarDto (Car car) ; }
总结 使用起来还是挺方便的,只需要新建一个映射器即可使用,不同的类型或者字段也能标记映射,阅读性更高,维护性也高。