当前位置: 首页 > 科技观察

六款常用的Bean复制工具一览

时间:2023-03-22 14:48:39 科技观察

在日常工作中,我们经常需要对对象进行复制或转换。比如传递参数时,将PO中输入的DTO转换成数据库,将PO返回给前端再转换成VO。再细分的话,可能会有DO(DomainObject)、TO(TransferObject)、BO(businessobject)等对象,随着业务的划分越来越细,对象的复制也越来越多比较频繁,所以本文将梳理一下常用的对象复制工具及其区别。常用工具大致如下:ApacheBeanUtilsSpringBeanUtilscglibBeanCopierHutoolBeanUtilMapstructDozer准备,创建两个类PO和DTO:@DatapublicclassOrderPO{Integerid;StringorderNumber;ListproId;}@DatapublicclassOrderDTO{intid;StringorderNumber;List<字符串>proId;}01。ApacheBeanUtils引入依赖坐标:commons-beanutilscommons-beanutils1.9.3来测试,初始化PO对象,并创建一个DTO空对象,使用BeanUtils:@org.junit.Testpublicvoidtest(){OrderPOorderPO=newOrderPO();orderPO.setId(1);orderPO.setOrderNumber("orderNumber");ArrayListlist=newArrayList(){{add("1");add("2");}};orderPO.setProId(list);OrderDTOorderDTO=newOrderDTO();BeanUtils.copyProperties(orderDTO,orderPO);}打印两个对象同样的属性:OrderPO(id=1,orderNumberorderNumber=orderNumber,proId=[1,2])OrderDTO(id=1,orderNumberorderNumber=orderNumber,proId=[1,2])可见,当Bean中的同名属性为基本数据类型和包装类,如int、Integer等时,可以正常复制。Bean进程中,使用的是深拷贝还是浅拷贝?两个List对象使用的是同一个对象,所以在拷贝中,如果有引用对象,则使用浅拷贝。复制完成后,如果再次修改对象:list.add("3");log.info(orderDTO.getProId());再次打印DTO对象,发现即使不再次复制也会加上修改后的值以前OrderDTO(id=1,orderNumberorderNumber=orderNumber,proId=[1,2,3])02。SpringBeanUtils在使用spring项目时不需要单独引入依赖。单独使用时需要引入坐标:org.springframeworkspring-beans5.2.2.RELEASE使用方法与apache的BeanUtils方法名相同,只是参数顺序颠倒,第一个参数为源对象,第二个参数为目标对象:BeanUtils.copyProperties(orderPO,orderDTO);过程省略,这里使用浅拷贝。Spring的BeanUtils也提供了额外的方法,这种可变参数方法可以忽略某些属性进行复制:voidcopyProperties(Objectsource,Objecttarget,String...ignoreProperties);忽略用于复制的orderNumber属性:BeanUtils.copyProperties(orderPO,orderDTO,"orderNumber");输出结果:OrderPO(id=1,orderNumberorderNumber=orderNumber,proId=[1,2])OrderDTO(id=1,orderNumber=null,proId=[1,2])另外,在阿里巴巴的开发手册中,它是强制性的,以避免使用apacheBeanUtils进行复制。推荐使用下面介绍的SpringBeanUtils或者BeanCopier。主要原因是Spring不像Apache那样检查反射。此外,SpringBeanUtils内部使用缓存来加速转换。另外,由于我们大部分项目都集成了Spring,如果没有其他特殊需求,直接使用它的BeanUtils就可以满足我们的基本需求。03.cglibBeanCopier如果项目中包含spring-core包的依赖,则不需要引入额外的依赖,否则需要引入坐标:cglibcglib3.3.0使用示例:BeanCopierbeanCopier=BeanCopier.create(orderPO.getClass(),orderDTO.getClass(),false);beanCopier.copy(orderPO,orderDTO,null);测试结果:OrderPO(id=1,orderNumberorderNumber=orderNumber,proId=[1,2])OrderDTO(id=0,orderNumberorderNumber=orderNumber,proId=[1,2])上面例子中id字段为不是正常复制的,这两个字段的区别在于PO中使用的是包装类型Integer,而DTO中使用的是基本类型int。所以在使用BeanCopier的时候,如果有基本类型和包装类,是不能正常复制的,只有改成相同类型后才能正常复制。另外BeanCopier还是采用了浅拷贝,验证过程中大家可以自己试验。04.HutoolBeanUtilhutool是个人常用的工具包。封装了文件、加解密、转码、正则化、线程、XML等JDK方法,还可以复制对象。使用前引入坐标:cn.hutoolhutool-all5.1.0使用方法如下,所用的同样是浅拷贝方法:BeanUtil.copyProperties(orderPO,orderDTO);和SpringBeanUtils一样,也可以忽略属性:voidcopyProperties(Objectsource,Objecttarget,String...ignoreProperties);另外,hutool的BeanUtil还提供了很多其他的实用方法:个人感觉Bean和Map的相互转换在使用上还是很常见的。有时候用Map接收参数的时候,后面再把Map转成Bean就很方便了。不同的,因为上面的方法,spring和apache,hutool都是使用了反射,cglib是基于字节码文件的操作,都是在代码运行的过程中动态执行的,但是Mapstruct不同,它是在编译的时候编译的生成复制Bean属性的代码,运行时无需使用反射或字节码技术,性能高。使用Mapstruct需要引入如下依赖:org.mapstructmapstruct-jdk81.3.0.Finalorg.mapstructmapstruct-processor1.3.0.Final需要额外写一个接口实现:@MapperpublicinterfaceConvertMapper{OrderDTOpo2Dto(OrderPOorderPO);}这里的@Mapper注解不是mybatis的注解,而是org.mapstruct.Mapper。使用起来也很简单:ConvertMappermapper=Mappers.getMapper(ConvertMapper.class);OrderDTOorderDTO=mapper.po2Dto(orderPO);查看编译后的目标目录,转换编译时我们定义的ConvertMapper接口,生成ConvertMapperImpl实现类,并在实现中添加po2Dto方法。看一下编译后的文件:可以看到方法中为每个属性生成了set方法,并为引用对象生成了一个新的对象,使用的是深拷贝的方式,所以这里的值在调用的时候也发生了变化先前的参考对象被修改。不会改变。此外,这种使用set/get的方式比使用反射更快。06.DozerDozer是一个Bean-to-Bean映射器,它递归地将数据从一个对象复制到另一个对象,这些bean可以有不同的复杂类型。使用前引入依赖坐标:net.sf.dozerdozer5.4.0调用方式很简单:DozerBeanMappermapper=newDozerBeanMapper();OrderDTOorderDTO=mapper.map(orderPO,OrderDTO.class);查看运行时生成的对象,可以看到使用的深拷贝方式:另外,还可以配置不同属性名的映射,修改DTO和PO,为PO添加一个name属性,为DTO添加一个value属性:@DatapublicclassOrderPO{Integerid;StringorderNumber;ListproId;Stringname;}@DatapublicclassOrderDTO{intid;StringorderNumber;ListproId;Stringvalue;}新建一个配置文件,mapping中可以添加字段的映射关系:com.cn.entity.OrderPOcom.cn.entity.OrderDTOnamevalueDozerBeanMapper使用上面的配置文件配置复制对象再次:...orderPO.setName("hydra");DozerBeanMappermapper=newDozerBeanMapper();Listma??ppingFiles=newArrayList<>();mappingFiles.add("dozer.xml");mapper.setMappingFiles(mappingFiles);OrderDTOorderDTO=mapper.map(orderPO,OrderDTO.class);查看测试结果,不同名称的字段也可以复制:OrderPO(id=1,orderNumberorderNumber=orderNumber,proId=[1,2],name=hydra)OrderDTO(id=1,orderNumberorderNumber=orderNumber,proId=[1,2],value=hydra)如果业务场景中的Bean有很多不同的属性,这样配置还是很麻烦的,需要手写很多xml文件。接触到的几种对象拷贝工具,在具体使用中,更多需要结合拷贝效率等需求,工作场景中需要用到深拷贝或浅拷贝等诸多因素。

最新推荐
猜你喜欢