项目中用到对象拷贝,做一个修改历史记录保存的功能,使用Spring AOP自定义注解实现修改记录的保存,历史记录表和业务表的字段差不多,保存的时候需要用到对象拷贝。下面是一些对象拷贝的工具,前3个都用过,这次想尝试一下新的mapstruct,喜新厌旧一波。
1. 对象拷贝常见的工具类
1.1 Apache 的BeanUtils的copyProperties方法
特点:浅拷贝,属性还是指向原本对象的引用。字段名称相同,类型不同无法进行赋值,基本类型字段和引用对象可以映射。
1.2 SpringUtils的copyProperties方法
特点:同Apache,效率比Apache高,参数的位置与Apache不同
1.3 序列化(JSON)
特点:深拷贝,性能低,耗时长(序列化-反序列化)。基本类和引用对象可以映射。字段名称相同,类型不同可以赋值。
1.4 MapStruct
特点:深拷贝,灵活(可自主配置字段映射关系),可以配置多对一映射关系。效率高,准确(编译器代码生成,源码就是get、set方法)。
2. MapStruct初次使用配置过程
2.1典型的常规操作,这里记录我配置过程中遇到的问题,仅供参考,可以不看,直接看后面的正确配置;
2.2决定要用,就先Bing了一把,大体一看内容还算完整,有配置,有测试,还有代码和源码,一看感觉不难,哐哐哐一顿配置,然后不出意外的报错了;
2.3我用的是公司封装的框架,耦合高,有几点和网上说的不一样:
一个是我的实体类有继承;
二个是我继承的实体类没有用lombok插件,我用了;
这两点对我造成了困扰,但是不影响使用MapStruct。
遇到的异常有:
一个是“No property named “XXX“ exists in source parameter(s). Did you mean news”;
No property named “XXX“ exists in source parameter(s). Did you mean news
一个是lombok插件的问题,提示java找不到符号(解决第一个问题过程中,越解决问题越严重);
2.4配置过程
一开始,导入maven依赖,根据网上的博文,注意lombok和mapstruct的版本,选择了同一个年份的版本。这里我用的是lombok插件,查看版本后选择的mapstruct版本,发布的年份一样,但是月份不一样,就配置上了。
然后就可以添加代码,写了Mapper和拷贝方法,就开始单元测试,问题来了,提示No property named “XXX“ exists in source parameter(s),虽然属性我可以点进去,但是就是报错。我怀疑是版本问题,就开始重新捣鼓版本,各种尝试,没解决。只好去官网,发现官网上的配置还添加了maven-compiler-plugin插件,我也加上,然而问题更严重了,冲突更厉害,直接影响我的lombok代码,改了3个d插的件版本,未解决,只能还原回去,确认就是版本问题。因为MapStruct原理类似于lombok,都是在编译期进行实现。只能继续看官网文档,最终配置成功。有问题找官网准没错。
3. MapStruct的正确配置,这里介绍maven方式
3.1项目pom文件添加依赖和plugins
<properties>
<java.version>1.8</java.version>
<org.mapstruct.version>1.5.5.Final</org.mapstruct.version>
<org.projectlombok.version>1.18.26</org.projectlombok.version>
<lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version>
</properties>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${org.projectlombok.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.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>${org.projectlombok.version}</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>${lombok-mapstruct-binding.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
3.2编写Mapper,我这里就用官网的代码了
CarMapper.java
@Mapper
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper( CarMapper.class );
@Mapping(source = "numberOfSeats", target = "seatCount")
CarDto carToCarDto(Car car);
}
Car.java
public class Car {
private String make;
private int numberOfSeats;
private CarType type;
//constructor, getters, setters etc.
}
CatDto.java
public class CarDto {
private String make;
private int seatCount;
private String type;
//constructor, getters, setters etc.
}
测试方法:
@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" );
}
4.总结:初次使用,没啥经验总结,这个项目也是简单使用,如有机会再深入了解。
4.1 lombok版本和MapStruct版本可以不必严格保持时间上对应。我这里用的就是最新的MapStruct版本和我IDE中lombok的插件版本,并不冲突。
4.2 如果两个对象中变量命名一致可以不用配置@Mapping。
/**
* 这个方法就是用于实现对象属性复制的方法
*
* @param xx 这个参数就是源对象,也就是需要被复制的对象
* @return 返回的是目标对象,就是最终的结果对象
* @Mapping 用来定义属性复制规则 source 指定源对象属性 target指定目标对象属性
*/
@Mappings({@Mapping(source = "id", target = "detailId")})
4.3有事找官网
官网:MapStruct – Java bean mappings, the easy way!
再不行找官网的例子:
GitHub - mapstruct/mapstruct-examples: Examples for using MapStruct
4.4 maven-compiler-plugin插件的版本根据你自己需要选择,不影响。主要的是annotationProcessorPaths的配置,我参考的是官方例子里面的mapstruct-lombok。