告别BeanUtils,Mapstruct从入门到精通

网站建设4年前发布
58 0 0

对象之间的属性拷贝,之前用的是Spring的BeanUtils,有一次,在学习领域驱动设计的时候,看了一位大佬的文章,他在文章中提到使用Mapstruct做DO和Entity的相互转换,出于好奇,后来就去了解了一下Mapstruct,发现这个工具确实优秀,所以果断弃用BeanUtils。
,如果你现在还在使用BeanUtils,看了本文,也会像我一样,从此改用Mapstruct。
,先上结论,Mapstruct的性能远远高于BeanUtils,这应该是大佬使用Mapstruct的主要原因,下面是我的测试结果,可以看出随着属性个数的增加,BeanUtils的耗时也在增加,并且BeanUtils的耗时跟属性个数成正比,而Mapstruct的耗时却一直是1秒,所以从对比数据可以看出Mapstruct是非常优秀的,其性能远远超过BeanUtils。
,下文会讲到Mapstruct性能好的根本原因。
,20230306015327d70b3f79206ba800208834c6380c6935679f75176,使用Mapstruct需要依赖的包如下,mapstruct、mapstruct-processor、lombok,可以去仓库中查看最新版本。,下面我们先来看下Mapstruct最简单的使用方式。
,当两个对象的属性类型和名称完全相同时,Mapstruct会自动拷贝;假设我们现在需要把UserPo的属性值拷贝到UserEntity中,我们需要做下面几件事情:
,定义mapstruct接口,在接口上打上@Mapper注解。
,接口中有一个常量和一个方法,常量的值是接口的实现类,这个实现类是Mapstruct默认帮我们实现的,下文会讲到。定义了一个po2entity的转换方法,表示把入参UserPo对象,转换成UserEntity。
,注意@Mapper是Mapstruct的注解,不要引错了。,创建一个UserPo对象,并使用Mapstruct做转化。,可以看到,所有赋值的属性都做了处理,且两边的值都一样,结果符合预期。
,20230306015328d88c9e5566bc3921d8c363b26bf73ca59c69a6433,Java程序执行的过程,是由编译器先把java文件编译成class字节码文件,然后由JVM去解释执行class文件。Mapstruct正是在java文件到class这一步帮我们实现了转换方法,即做了预处理,提前编译好文件,如果用过lombok的同学一定能理解其好处,通过查看class文件,可以看出IPersonMapper被打上org.mapstruct.Mapper注解后,编译器自动会帮我们生成一个实现类IPersonMapperImpl,并实现了po2entity这个方法,看下面的截图。
,从生成的代码可以看出,转化过程非常简单,只使用了UserPo的get方法和UserEntity的set方法,没有复杂的逻辑处理,清晰明了,所以性能很高。
,下面再去看BeanUtils的默认实现。
,20230306095542394f3b076ce2ad2aa3b17635d7c27801bb8b5e868,BeanUtils部分源码如下,转换的原理是使用的反射,反射的效率相对来说是低的,因为jvm优化在这种场景下有可能无效,所以在对性能要求很高或者经常被调用的程序中,尽量不要使用。我们平时在研发过程中,也会遵守这个原则,非必要,不反射。
,从下面的BeanUtils代码中可以看出,转化逻辑非常复杂,有很多的遍历,去获取属性,获取方法,设置方法可访问,然后执行,所以执行效率相对Mapstruct来说,是非常低的。回头看Mapstruct自动生成的实现类,简洁、高效。,对于属性名称不同的属性进行处理时,需要使用@Mapping,比如修改UserEntity中的userNick为userNick1,然后进行转换。
,@Mapping(target = "userNick1", source = "userNick"),此处的意思就是在转化的过程中,将UserPo的userNick属性值赋值给UserEntity的userNick1属性。,可以看到,正常映射,符合预期。
,2023030609554464fe4f845d63a7fb97f4673cfde122dcbc354d682,我们再来看实现类,可以看到,Mapstruct帮我们做了处理,把po的userNick属性赋值给了entity的userNick1。
,202303060153297323de8497ff45cfc302644eb561cbfffadceb507,2023030601533061f7b8872c0577b414a7535d77e8fc856feb29699,如果现有的能力都不能满足需要,可以自定义一个转换器,比如我们需要把一个字符串使用JSON工具转换成对象。
,我们在po中加入一个字符串的attributes属性,在entity中加入Attributes类型的属性,转换器很简单,就是一个普通的Java类,只要在方法上打上Mapstruct的注解@Named。,可以看出我们将把String转成了JSON对象,20230306015330337511631a12e966831580390a163d5cb9ca26474,可以看到,在实现类中Mapstruct帮我们new了一个AttributeConvertUtil的对象,并调用了该对象的jsonToObject方法,将字符串转成JSON,最终赋值给了UserEntity的attributes属性,实现很简单,也是我们可以猜到的。
,20230306015331a77988c72072c784245905889ad90db584cac6603,代码很简单,循环的创建UserPo对象,使用两种方式,转换成UserEntity对象,最终输出两种方式的执行耗时。可以加减属性或者修改转换次数,对比不同场景下的执行耗时。,20230306015332b47f44b396c9fb239e718340600b89e42c7e3d907,通过本次调研,Mapstruct的高性能是毋庸置疑的,这也是我选择使用他的根本原因。在使用方式上和BeanUtils对比,Mapstruct需要创建mapper接口和自定义转换工具类,其实上手成本并不高,但是我们换取了高性能,这是非常值得的,所以强烈推荐大家使用Mapstruct,是时候和BeanUtils说再见了。
,保持好奇,不断探索,让程序更友好!
,TMALL CAMPUS (天猫校园) 是阿里巴巴旗下重要的业务单元,天猫校园整合阿里巴巴大生态,将新理念、新技术、新业态、新模式落地到校园,为师生提供多方位、多形态的服务,协助高校后勤服务升级;致力于打造购物、学习、生活、实践为一体的校园生活新方式,实现校园商业的服务育人。
,天猫校园,让校园学习生活更美好

© 版权声明

相关文章