Java对象拷贝原理剖析及最佳实践
作者:互联网
1序言
目标复制,是在研发流程中,避不开的一个过程,既存在Po、Dto、Do、Vo每个表现层数据库的变换,也存在系统交互如实例化、反序列化。
Java对象复制分成深拷贝和浅拷贝,现阶段常见的特性复制专用工具,包含Apache的BeanUtils、Spring的BeanUtils、Cglib的BeanCopier、mapstruct全是浅拷贝。
1.1深拷贝
深拷贝:对基本数据类型开展值传递,对引用数据类型,建立一个新的目标,并拷贝内容称之为深拷贝。
深拷贝普遍有如下四种控制方式:
对象
Serializable实例化
完成Cloneable接口
JSON实例化
1.2浅拷贝
浅拷贝:对基本数据类型开展值传递,对引用数据类型开展引用传递一样的复制称之为浅拷贝。根据完成Cloneabe接口并重新写过Object类中的clone()方式能够实现浅复制。
2常见目标复制专用工具基本原理分析及性能对比
现阶段常见的特性复制专用工具,包含Apache的BeanUtils、Spring的BeanUtils、Cglib的BeanCopier、mapstruct。
ApacheBeanUtils:BeanUtils是Apachecommons部件里边中的一员,由Apache给予的一套开源系统api,用以简单化对javaBean操作,可以对基本类型全自动变换。
SpringBeanUtils:BeanUtils是spring框架下带有的专用工具,在org.springframework.beans包下,spring新项目能够直接用。
CglibBeanCopier:cglib(CodeGenerationLibrary)是一个强悍的、性能卓越、高质量代码生成类库,BeanCopier依托cglib的字节码增强能力,动态生成实现类,进行对象复制。
mapstruct:mapstruct是一个Java注解Cpu,用以形成类型安全的Bean投射类,在建立时,依据注释形成实现类,进行目标复制。
2.1基本原理剖析
2.1.1ApacheBeanUtils
使用方法:BeanUtils.copyProperties(target,source);
BeanUtils.copyProperties目标复制的关键编码如下所示:
//1.获得源对象的属性叙述
PropertyDescriptor[]origDescriptors=this.getPropertyUtils().getPropertyDescriptors(orig);
PropertyDescriptor[]temp=origDescriptors;
intlength=origDescriptors.length;
Stringname;
Objectvalue;
//2.循环系统获得源目标每一个特性,设定目标对象基础属性
for(inti=0;iPropertyDescriptororigDescriptor=temp[i];
name=origDescriptor.getName();
//3.校检源目标字段名能读切目标对象该字段名应写
if(!"class".equals(name)&&this.getPropertyUtils().isReadable(orig,name)&&this.getPropertyUtils().isWriteable(dest,name)){
try{
//4.获得源目标字段值
value=this.getPropertyUtils().getSimpleProperty(orig,name);
//5.复制特性
this.copyProperty(dest,name,value);
}catch(NoSuchMethodExceptionvar10){
}
}
}
循环遍历源对象每一个特性,对于每一个特性,复制步骤为:
校检由来类字段名是否可以读isReadable
校检总体目标类字段名是否可以写isWriteable
获得由来类字段名基础属性getSimpleProperty
获得总体目标类字段种类type,然后进行数据转换
设定总体目标类字段值
因为一个字段复制时每一个阶段都是会启用PropertyUtilsBean.getPropertyDescriptor获得特性配备,而此方法根据for循环获得类字段名特性,严重危害复制高效率。
获得字段名特性配备的关键编码如下所示:
PropertyDescriptor[]descriptors=this.getPropertyDescriptors(bean);
if(descriptors!=null){
for(inti=0;iif(name.equals(descriptors[i].getName())){
returndescriptors[i];
}
}
}
2.1.2SpringBeanUtils
使用方法:BeanUtils.copyProperties(source,target);
BeanUtils.copyProperties关键编码如下所示:
PropertyDescriptor[]targetPds=getPropertyDescriptors(actualEditable);
ListignoreList=ignoreProperties!=null?Arrays.asList(ignoreProperties):null;
PropertyDescriptor[]arr$=targetPds;
intlen$=targetPds.length;
for(inti$=0;i$PropertyDescriptortargetPd=arr$[i$];
MethodwriteMethod=targetPd.getWriteMethod();
if(writeMethod!=null&&(ignoreList==null||!ignoreList.contains(targetPd.getName()))){
PropertyDescriptorsourcePd=getPropertyDescriptor(source.getClass(),targetPd.getName());
if(sourcePd!=null){
MethodreadMethod=sourcePd.getReadMethod();
if(readMethod!=null&&ClassUtils.isAssignable(writeMethod.getParameterTypes()[0],readMethod.getReturnType())){
try{
if(!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())){
readMethod.setAccessible(true);
}
Objectvalue=readMethod.invoke(source);
if(!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())){
writeMethod.setAccessible(true);
}
writeMethod.invoke(target,value);
}catch(Throwablevar15){
thrownewFatalBeanException("Couldnotcopyproperty'"+targetPd.getName()+"'fromsourcetotarget",var15);
}
}
}
}
}