您好!欢迎来到爱源码

爱源码

热门搜索: 抖音快手短视频下载   

七个对象复制工具中,你最喜欢哪一个? <影视源码>

  • 时间:2022-07-07 01:21 编辑: 来源: 阅读:306
  • 扫一扫,手机访问
摘要:七个对象复制工具中,你最喜欢哪一个? <影视源码>
在日常编程中,我们经常会遇到对象属性复制的场景,比如下面常见的三层MVC架构。 Image.png当我们在上述架构下编程时,我们通常需要经历对象转换。例如,在业务请求流程通过三层结构后,需要将其DTO转换为DO,然后存储在数据库中。 当需要从数据查询页面显示时,查询数据会通过三层架构从DO转换到DTO,最后转换到VO,然后显示在页面上。 业务简单的时候,我们手工写代码,通过getter/setter复制对象属性,非常简单。 但是一旦业务变得复杂,对象属性变得很多,那么手写代码就会成为程序员的噩梦。 手写不仅繁琐费时,而且容易出错。 a粉之前经历过一个项目,一个对象大概有四五十个字段属性。那时候A粉刚入门,什么都不懂。他写了半天getter/setter复制对象属性。 画外音:一个物体有这么多属性,显然不公平。我们应该在设计过程中把它分开。 直到这时,A粉才明白了对象属性复制工具类。他用了之后发现就是这个道理,再也不用手写代码了。 后来工具类越来越多。虽然核心功能相同,但还是有很多区别。 新手可能看起来不知所措,不知道如何选择。 所以一粉,今天这篇文章详细介绍一下市面上常用的工具类:Apache bean utils Spring bean utils CGlib bean copierdozorikamapstruct工具类特性。在详细介绍这些工具类之前,我们先来看下一个易于使用的属性复制工具类,需要哪些特性:基本属性复制,这是不同类型基本函数的属性赋值,比如基本类型及其封装类型等不同字段名的属性赋值。当然,字段名称应该尽可能一致。但在实际业务中,这些原因可能会导致字段名不一致,比如浅拷贝/深拷贝。浅表副本将引用同一个对象。如果不小心同时换了对象,就会踏入一个意想不到的坑。让我们开始详细的工具类。 Apache BeanUtils第一个细节是第一个工具类“Apache BeanUtils”,应该是Java领域属性复制最著名的工具类。这个工具类想必很多人或多或少都用过或见过。 没用过也没关系。让我们展示一下这个类的用法。用法很简单。 首先,我们介绍依赖关系,这里我们使用最新版本: StudentDTO StudentDTO = new StudentDTO();studentdto . setname(" A Pink ");studentdto . setage(18);studentdto . setno(" 6666 ");列表& lt字符串& gtsubjects = new ArrayList & lt& gt();subjects . add(" math ");subjects.add("英语");studentDTO.setSubjects(科目);studentDTO.setCourse(新课程(“CS-1”));studentdto . set createdate(" 2020-08-08 ");student do student do = new student do();bean utils . copy properties(studentDO,studentDTO);但是,如果你这样写上面的代码,我们会遇到第一个问题。默认情况下,BeanUtils不支持字符串到日期类型的转换。 为了在image.png解决这个问题,我们需要自己构造一个转换器转换类,然后用ConvertUtils注册它。使用方法如下:convertutils . register(new converter(){ @ sneaky trows @ override public < Date & gt;日期转换(Class & lt日期& gttype,Object value){ if(value = = null){ return null;} if(value instance of String){ String str =(String)value;return(Date)dateutils . parse Date(str," yyyy-MM-DD ");}返回null}},date . class);此时,我们观察studentDO和studentDTO对象的属性值:image.png从上图中,我们可以从BeanUtils中得出少量结论:公共字段名不一致的属性不能复制到嵌套的对象字段中,会使用同一个对象作为源对象。即使使用浅拷贝类型不一致的字段,也会转换默认类型。 虽然BeanUtils用起来很方便,但是它的底层源代码,为了追求完美,增加了太多的包装,使用了很多反射,做了很多检查,导致性能很差。所以在阿里巴巴的开发手册Apache BeanUtils中强制避免使用。 image . png image-2020081822213879 Spring bean utils Spring属性复制工具类名和Apache一样,基本用法也差不多。 我先来看看SpringBeanUtils的基本用法。 同样,我们先介绍一下依赖。从名字就可以看出,BeanUtils位于Spring-Beans模块中,在这里我们仍然使用最新的模块。 & lt依赖性& gt& ltgroupId & gtorg.springframework & lt/groupId & gt;& ltartifactId & gt春豆& lt/artifact id & gt;& lt版本& gt发布<。/version & gt;& lt/dependency & gt;这里,我们使用DTO来重用上面的DO示例。转换代码如下://省略上面的赋值代码,与上面一致。学生做学生做=新学生做();bean utils . copy properties(studentDTO,studentDO);从用法上可以看出,Spring BeanUtils和Apache有一个最大的区别。两者的源对象和目标对象参数位于不同的位置。a粉之前没注意,按照Apache的用法用了Spring工具类。 此时,比较studentDO和studentto对象:image.png。从上面的对比图可以得出几个结论:字段名不一致,属性无法复制,类型不一致,属性无法复制。 但是,如果类型是基本类型和基本类型包装类,这种可以转换的嵌套对象字段将使用与源对象相同的对象,即使使用了浅复制。除了这个方法之外,Spring BeanUtils还提供了一个重载方法:public static void copy properties(对象源、对象、字符串...忽略属性)使用这个方法,我们可以忽略一些不想复制过去的属性:bean utils . copy properties(studentdto,studentdo," name ");这样,name属性就不会被复制到DO对象中。 虽然image.png Spring Nutils的功能与Apache Nutils相似,但Spring Nutils的性能是Apache Nutils中最好的。 主要原因是Spring不像Apache那样过多的使用反射来检查,Spring BeanUtils使用缓存来加速转换。 所以推荐Spring BeanUtils作为两者之间的选择。 以上两个Cglib BeanCopier是A Pink日常工作中经常用到的,下面几个是A Pink最近才接触的,比如Cglib BeanCopier。 这个方法可能比上面两个类稍微复杂一点。我们来看看具体用法:首先介绍一下Cglib依赖:< dependency & gt& ltgroupId & gtcglib & lt/groupId & gt;& ltartifactId & gtcglib & lt/artifact id & gt;& lt版本& gt3 . 3 . 0 & lt;/version & gt;& lt/dependency & gt;画外音:如果您的项目中仍然有Spring-Core,如果您查找BeanCopier类,您可以找到两个同名的不同包。 一个属于Cglib,一个属于Spring-Core。 实际上,Spring-Core中的BeanCopier实际上引入了Cglib中的类。这样做的目的是为了保证Cglib相关类在Spring的使用长度内的稳定性,防止外部Cglib依赖的不一致导致Spring运行异常。 转换代码如下://省略赋值语句student do student do = new student do();bean copier bean copier = bean copier . create(student to . class,StudentDO.class,false);beanCopier.copy(studentDTO,studentDO,null);与BeanUtils相比,通过使用该方法,BeanCopier稍微先进一些。 比较studentDO和studentDTO对象:image.png从上面可以得到与Spring Beanutils基本一致的结论:字段名不一致,属性不可复制,类型不一致,属性不可复制。 不过,有点不一样。如果类型是包装类型的基本类型/基本类型,则两者不能复制。 嵌套对象字段将使用与源对象相同的对象。即使我们在浅层副本上使用Beanutils,我们也没有好的方法做到这一点,因为字段名和类型是不一致的。我们只能手写硬代码。 但是,在BeanCopier下,我们可以引入一个用于类型转换的转换器。 //注意最后一个属性设置为true bean copier bean copier = bean copier . create(studentdto . class,studentdo.class,true);//设置转换器beancopier.copy (studentdto,studentdo,new converter(){ @ override public Object convert(Object source,classtarget,Object context){ if(source instance of Integer){ Integer num =(Integer)source;返回num . tostring();}返回null}});但是吐槽一下这个转换器。一旦我们自己打开并使用它,我们需要自己复制所有属性。 例如,在上面的例子中,我们只解决了源对象字段类型为整数时的问题,其他都没有解决。 我们得到DO对象,只能复制name属性。 image.pngCglib BeanCopier的原理与上面两个Beanutils原理不同。它主要利用字节码技术动态生成一个代理类,实现get和set方法。 在生成代理类的过程中有一定的开销,但是一旦生成,我们可以缓存并重用它。所有Cglib的性能都优于以上两种Beanutils的性能。 DozerDozer,中文直译为挖掘机,是一个“重量级”属性复制工具类。与上述三个工具类别相比,Dozer具有许多强大的功能。 Image.png logo画外音:重量级/轻量级其实是一个相对的名词,因为Dozer相对于BeanUtils这样的工具有很多高级的功能,所以它是一个重量级的工具类。 一个粉一遇到这个工具类,就印象深刻。真的很强大,Dozer已经实现了我们上面预期的所有功能。 我们来看看用法。首先,我们介绍推土机依赖性: 画外音:从下面的代码中我们可以看到,需要加载配置文件来生成DozerBeanMapper实例,随便生成代价很大。 在我们的应用程序中,我们应该使用singleton模式并重用DozerBeanMapper。 如果属性是一些简单的基本类型,那么我们只需使用上面的代码就可以快速复制属性。 然而,不幸的是,我们的代码有字符串和日期类型之间的转换。如果我们直接使用上面的代码,程序会运行异常。 所以在image.png这里,我们需要使用Dozer强大的配置功能,总共可以使用以下三种方式:XMLAPI注释,其中,API方法比较繁琐,目前大部分都是用XML进行的。另外,注解功能是Dozer 5.3.2之后新加入的功能,但功能相比XML较弱。 XML的用法:让我们使用XML配置方法来配置d to和DO之间的关系。首先,我们创建一个新的dozer/dozer-mapping.xml文件:<?xml版本="1.0 "编码="UTF-8 "?& gt& lt映射xmlns = " http://dozer . SourceForge . net " xmlns:xsi = " http://www . w3 . org/2001/XML schema-instance " xsi:schema location = " http://dozer.sourceforge.net·http://dozer.sourceforge.net/schema/beanmapping.xsd" & lt;!-类级日期转换,默认使用这种格式转换->:& lt;映射日期-格式= " yyyy-MM-DD HH:MM:ss " & gt;& lta级& gtcom just . doone . example . domain . studentdto & lt;/class-a & gt;& ltb类& gtcom . just . doone . example . domain . student do & lt;/class-b & gt;& lt!-指定下面字段名不一致的映射关系->:& lt;field & gt& lta & gt否& lt/a & gt;& ltb & gt号码& lt/b & gt;& lt/field & gt;& ltfield & gt& lt!-字段级的日期转换将覆盖字段级的转换->:& lt;a date-format = " YY-MM-DD " & gt;创建日期& lt/a & gt;& ltb & gt创建日期& lt/b & gt;& lt/field & gt;& lt/mapping & gt;& lt/mappings & gt;然后修改我们的Java代码,添加读取Dozer的配置文件:Dozer bean mapper mapper = new Dozer bean mapper();列表& lt字符串& gtmappingFiles = new ArrayList & lt& gt();//读取配置文件mapping files . add(" dozer/dozer-mapping . XML ");mapper . setmappingfiles(mapping files);student do student do = mapper . map(student dto,student do . class);system . out . println(student do);运行后,比较studentDO和studentto对象:image.png。从上图可以发现,不同类型的字段的属性都被复制了。DO和DTO对象字段不是同一个对象,即深度复制。通过配置字段名的映射关系,不同字段的属性也被复制。除了这些相对简单的属性,Dozer还支持许多附加功能,如枚举属性复制、地图和其他集合属性复制等。 有些image.png的朋友刚刚看到Dozer的用法,可能会觉得这个工具类比较繁琐,不像BeanUtils工具类,一行代码就能解决。 实际上,Dozer可以很好地与Spring框架集成。我们可以在Spring配置文件里提前配置,然后只需要引用Dozer对应的Bean,也是一行代码。 Dozer与Spring集成,我们可以使用它的DozerBeanMapperFactoryBean,配置如下::& ltproperty name="customConverters " >& lt列表& gt& ltbean class = " org . dozer . converters . custom converter "/& gt;& lt/list & gt;& lt/property & gt;& lt/bean & gt;DozerBeanMapperFactoryBean支持设置许多属性,因此您可以自己设置类型转换和其他属性。 还有一个简单的方法,我们可以用XML配置dozer bean mapper:< bean id = " org . dozer . mapper " class = " org . dozer . dozer bean mapper " & gt;& ltproperty name="mappingFiles " >& lt列表& gt& lt值& gtdozer/dozer-mapperpping . XML & lt;/value & gt;& lt/list & gt;& lt/property & gt;& lt/bean & gt;Spring配置完成后,我们可以直接注入:@ autowireMapper Mapperpublic obj mapping(studentdto studentdto){//直接使用studentdo studentdo = mapper . map(studentdto,studentdo . class);}标注模式Dozer标注模式相比XML配置较弱,只能完成不一致字段名的映射。 在上面的代码中,我们可以在DTO的no字段上使用@Mapping注释,这样当我们使用Dozer完成转换时,字段属性就会被复制。 @ data public class studentd to { private String name;私有整数年龄;@Mapping("number ")私有字符串no;个人分发名单& lt字符串& gt科目;私人课程课程;私有字符串createDate}虽然目前标注功能有点弱,但是正式版可能以后会增加新的标注功能,XML和标注可以一起使用。 最后,Dozer的底层本质上是用反射来完成属性的复制,所以执行速度不是那么理想。 Orikaorika也是类似Dozer的重量级属性复制工具类,也提供类似Dozer等功能。 但是orika不需要使用复杂的XML配置,它提供了一套非常简洁的API用法,非常好用。 首先,我们介绍它的最新依赖项: 在上面的代码中,我们特意注释了DTO对象中createDate时间属性的设置值,因为默认情况下,如果没有单独设置时间类型转换器,上面的代码就会运行错误。 另外,在上面的代码中,字段名不一致的属性不会被复制,所以我们需要单独设置。 让我们设置一个时间转换器并指定字段名:Mapper Factory Mapper Factory = New default Mapper Factory。构建器()。build();converter factory converter factory = mapperfactory . getconverterfactory();converter factory . register converter(new datetostring converter(" yyyy-MM-DD "));mapperfactory . class map(student to . class,studentdo.class)。field ("no "," number ")//一定要默认调用。默认情况下()。寄存器();MapperFacade mapper = mapperfactory . getmapperfacade();student do student do = mapper . map(student dto,student do . class);在上面的代码中,首先我们需要在ConverterFactory中注册一个时间类型的转换器,其次我们需要在MapperFactory中指定不同字段名之间的映射关系。 在这里,我们应该注意,在我们使用classMap之后,如果我们希望相同的字段名属性被默认复制,我们必须调用byDefault方法。 简单对比一下DTO和DO对象:image.png上图显示了orika的少量特征:默认支持类型不一致(基本类型/包装类型)。转换支持深度复制,并指定不同的字段名映射关系,因此可以成功复制属性。 此外,orika还支持集合映射:mapper factory mapper factory = newdefaultmapper factory . builder()。build();列表& ltPerson & gtpersons = new ArrayList & lt& gt();列表& ltPersonDto & gtperson DTOs = mapperfactory . getmapperfacade()。mapAsList(persons,person dto . class);最后说一下orika的实现原理。orika与dozer的底部原理不同。在底层,它使用javassist生成字段属性的映射字节码,然后直接动态加载并执行字节码文件。相对于推土机的工具类,速度会快很多。 Image.pngMapStruct不知不觉中,一口气写了五个属性复制工具类,这里的朋友都看过,不要放弃,坚持看下去。这里有一个不同于上面的工具类“MapStruct”。 以上详细的工具类,无论是使用反射还是字节码技术,都需要在代码运行过程中动态执行,所以以上工具类的执行速度会比手工硬编码慢很多。 有没有运行速度和硬编码一样快的工具类?这是关于MapStruct这个工具类的细节。这个工具类之所以运行速度和硬编码一样快,是因为它在编译时生成了Java Bean属性复制的代码,在运行时不需要使用反射或者字节码技术,所以保证了高性能。 另外,由于代码是在编译时生成的,如果有疑问,可以在编译时提前暴露。对于开发者来说,可以提前处理问题,而不是等代码应用上线,运行后才发现错误。 让我们来看看如何使用这个工具类。首先,我们引入这种依赖性: 其次,因为我们的DTO和DO对象中的字段名不一致,所以我们也使用@Mapping注释在转换方法中指定字段映射。 另外,我们的createDate字段类型不一致,这里还需要指定时间格式类型。 以上定义完成后,我们可以直接使用StudentMapper行代码来完成对象转换。 //忽略其余代码studentdo studentdo = student mapper . instance . dto todo(studentdto);如果我们的对象使用Lombok并使用@Mapping来指定不同的字段名,那么编译时可能会抛出以下错误:image.png。原因是Lombok在编译时也需要自动生成代码,这可能会导致两者之间的冲突。MapStruct生成代码时,没有Lombok生成的代码。 解决方法是将Lombok添加到maven-compiler-plugin插件的配置中,如下所示: 由此可见,MapStruct的功能相当于手工设置getter/setter的值,所以性能会很好。 看完这篇文章,我们一共学习了7个属性复制工具类。我们如何选择这些工具类?a粉谈谈自己的一点小见解:首先,首先,我们直接抛弃Apache Beanutils吧。阿里巴巴的规格就不用说了,都是设定好的,我们不好好利用。 第二,当然要看工具的性能。关于这些工具的性能,有许多在线文章的细节。一个粉色的抄袭了他们,可以对比一下。 Image.png来自:https://www.hollischuang.com/archives/5337image.png来自:https://www . bael dung . com/Java-performance-mapping-frameworks可以看出MapStruct的性能相当优秀。 然后,如果您的业务需要高性能、高响应能力等。,或者你的业务有导入/导出大量数据的场景,而且这个代码有对象转换,就不要用Apache Beanutils和Dozer了。 第三,其实很大一部分应用并没有很高的性能要求,只要工具类能提供足够的便利,都是可以接受的。 如果你的业务没有复杂的需求,直接用Spring Beanutils就可以了。毕竟Spring的大部分包都用上了,剩下的包就不用导入了。 然后,如果有不同类型的服务,不同的字段名称,可以考虑使用orika这样的重量级工具。


  • 全部评论(0)
资讯详情页最新发布上方横幅
最新发布的资讯信息
【域名/主机/服务器|】qq邮箱提醒在哪里打开(2024-06-04 18:58)
【技术支持|常见问题】1556原创ng8文章搜索页面不齐(2024-05-01 14:43)
【技术支持|常见问题】1502企业站群-多域名跳转-多模板切换(2024-04-09 12:19)
【技术支持|常见问题】1126完美滑屏版视频只能显示10个(2024-03-29 13:37)
【技术支持|常见问题】响应式自适应代码(2024-03-24 14:23)
【技术支持|常见问题】1126完美滑屏版百度未授权使用地图api怎么办(2024-03-15 07:21)
【技术支持|常见问题】如何集成阿里通信短信接口(2024-02-19 21:48)
【技术支持|常见问题】算命网微信支付宝产品名称年份在哪修改?风水姻缘合婚配对_公司起名占卜八字算命算财运查吉凶源码(2024-01-07 12:27)
【域名/主机/服务器|】帝国CMS安装(2023-08-20 11:31)
【技术支持|常见问题】通过HTTPs测试Mozilla DNS {免费源码}(2022-11-04 10:37)

联系我们
Q Q:375457086
Q Q:526665408
电话:0755-84666665
微信:15999668636
联系客服
企业客服1 企业客服2 联系客服
86-755-84666665
手机版
手机版
扫一扫进手机版
返回顶部