目录
👋前言
👀一、环境准备
🌱二、拷贝工具使用
2.1 BeanUtils 使用
2.2 MapStruct 使用
💞️三、对比
📫四、章末
👋前言
小伙伴们大家好,最近在一些技术文章中看到了开发时经常接触的对象拷贝操作,实现该操作的话有很多方式,来探讨下常见的对象拷贝工具:MapStruct 和 Apache Commons BeanUtils
有哪些使用差异和哪些特性
👀一、环境准备
为了方便对比每种拷贝工具的使用,先创建好几个基础类
TestData.class 填充数据类,代码如下:
import lombok.Data;
@Data
public class TestData {
private Integer id;
public TestData(int i){
this.id = i;
}
}
Source.class 代表源数据类,代码如下:
import lombok.Data;
/**
* @author HuangBen
*/
@Data
public class Source {
private Integer id;
private String name;
private TestData data;
private Long createTime;
}
Target.class 代表目标数据类,代码如下:
@Data
public class Target {
private Long id;
private String name;
private TestData data;
private Long createTime;
}
🌱二、拷贝工具使用
2.1 BeanUtils 使用
BeanUtils 的使用比较简单,如下,先创建一个填充对象 testData(用于测试拷贝中涉及到的对象拷贝是深拷贝还是浅拷贝) ,然后创建源数据类,以及一个空的 target 对象,通过 BeanUtils.copyProperties(Source.class,Target.class) 方法将源数据类中的属性赋值给目标类对象:
拷贝后的结果如下,可以看到目标类中 Long 类型的 id 与源数据 Integer 类型的 id 拷贝出现 null 值,另外对于 testData 的拷贝属于浅拷贝类型:
注:浅拷贝简单来讲就是拷贝出来的对象依然指向原有对象的引用地址,所以两者 == 操作的时候返回 true,并且修改原有对象会一并影响拷贝对象
深拷贝出来的对象与原先对象没有联系,== 操作为 false,并且修改原有对象不会影响拷贝出来的对象
2.2 MapStruct 使用
2.2.1 mapStruct 是一个外部工具需要在 pom.xml 文件中手动导入依赖(版本可替换)
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.2.0.Final</version>
</dependency>
2.2.2 定义工具接口
这里注意 @Mapper 注解的来源是 mapstruct 包下,不是 mybatis 的注解
另外一个方法就是自定义的对象拷贝方法,从 Source 转换为 Target ,使用方式如下,该方式不需要提前创建对象,因为转换方法有返回值
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/**
* @author HuangBen
*/
@Mapper
public interface BeanMapper {
BeanMapper INSTANCE = Mappers.getMapper(BeanMapper.class);
Target map(Source source);
}
拷贝结果如下,可以看到从 Integer 类型转换为 long 类型的时候,mapstruct 会自动进行类型转换处理,并且默认也是浅拷贝:
2.2.3 MapStruct 不同类型转换成功分析
测试类编译后我们可以在target目录下找到帮我们生成的一个接口实现类BeanMapperImpl,如下:
在默认提供的实现类中会对不同类型的元素进行手动转换(前提支持这种类型转换)
💞️三、对比
BeanUtils | MapStruct | |
作用域 | 属于运行时的工具库,使用反射机制动态地操作 Java Bean 的属性,存在性能开销 | 基于编译时注解处理器的工具,在编译阶段生成映射代码,因此映射过程是类型安全的,并且在运行时性能较高。 |
性能 | 使用反射机制来进行属性复制,运行时性能比 MapStruct 略低 | 在编译时生成的代码(像编译后生成的默认实现类中存在指定的业务代码),因此性能较高,映射过程效率优秀。 |
依赖 | 可直接使用,实现简单 | 需要手动添加依赖引入,定义额外的处理过程 |
📫四、章末
文中这种使用方式只是简单的对象拷贝操作,更多功能可以参考官方文档
文章到这里就结束了~