分类: Java
2022-11-24 15:00:59
作者:宁海翔
对象拷贝,是我们在开发过程中,绕不开的过程,既存在于Po、Dto、Do、Vo各个表现层数据的转换,也存在于系统交互如序列化、反序列化。
Java对象拷贝分为深拷贝和浅拷贝,目前常用的属性拷贝工具,包括Apache的BeanUtils、Spring的BeanUtils、Cglib的BeanCopier、mapstruct都是浅拷贝。
深拷贝:对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容称为深拷贝。
深拷贝常见有以下四种实现方式:
浅拷贝:对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝称为浅拷贝。通过实现Cloneabe接口并重写Object类中的clone()方法可以实现浅克隆。
目前常用的属性拷贝工具,包括Apache的BeanUtils、Spring的BeanUtils、Cglib的BeanCopier、mapstruct。
使用方式:BeanUtils.copyProperties(target, source);
点击(此处)折叠或打开
BeanUtils.copyProperties 对象拷贝的核心代码如下:
循环遍历源对象的每个属性,对于每个属性,拷贝流程为:
由于单字段拷贝时每个阶段都会调用PropertyUtilsBean.getPropertyDescriptor获取属性配置,而该方法通过for循环获取类的字段属性,严重影响拷贝效率。
点击(此处)折叠或打开
获取字段属性配置的核心代码如下:
2.1.2 Spring BeanUtils
使用方式: BeanUtils.copyProperties(source, target);
点击(此处)折叠或打开
BeanUtils.copyProperties核心代码如下:
拷贝流程简要描述如下:
与Apache BeanUtils的属性拷贝相比,Spring通过Map缓存,避免了类的属性描述重复获取加载,通过懒加载,初次拷贝时加载所有属性描述。
使用方式:
点击(此处)折叠或打开
create调用链如下:
BeanCopier.create
-> BeanCopier.Generator.create
-> AbstractClassGenerator.create
->DefaultGeneratorStrategy.generate
-> BeanCopier.Generator.generateClass
BeanCopier 通过cglib动态代理操作字节码,生成一个复制类,触发点为BeanCopier.create
使用方式:
mapstruct基于注解,构建时自动生成实现类,调用链如下:
点击(此处)折叠或打开
点击(此处)折叠或打开
MappingProcessor.process -> MappingProcessor.processMapperElements
MapperCreationProcessor.process:生成实现类Mapper
MapperRenderingProcessor:将实现类mapper,写入文件,生成impl文件
使用时需要声明转换接口,例如:
生成的实现类如下:
2.2 性能对比
以航空业务系统中发货任务po到dto转换为例,随着拷贝数据量的增大,研究拷贝数据耗时情况
经过以上分析,随着数据量的增大,耗时整体呈上升趋势
使用时可以使用map缓存,减少同一类对象转换时,create次数
点击(此处)折叠或打开
3.2 mapstruct
mapstruct支持多种形式对象的映射,主要有下面几种
点击(此处)折叠或打开
以上就是我在使用对象拷贝过程中的一点浅谈。在日常系统开发过程中,要深究底层逻辑,哪怕发现一小点的改变能够使我们的系统更加稳定、顺畅,都是值得我们去改进的。
{BANNED}最佳后,希望随着我们的加入,系统会更加稳定、顺畅,我们会变得越来越优秀。