今天突然看到看到一句话:“计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决,如果不行,那就加两层”。看到这个就想到了java开发中接受和处理数据时的操作。在数据存储层,我们使用DO来抽象一个业务实体;在业务逻辑层,我们使用DTO来表示数据传输对象;到了展示层,我们又把对象封装成VO来与前端进行交互。在做这些交互时用到的一些工具,下面介绍一些自己使用过的对象转换的工具。
BeanUtils
在平时的业务中,我们封装那些交互的数据的时候,大部分会使用get、set来做封装,idea有一个lombok插件提供了data注解。在业务越来越复杂的今天,我们已经避免造轮子了。而是直接去使用。所以我们在做数据转换的时候大部分会使用别人封装好的工具。先介绍第一种-springframework下的BeanUtils。使用反射的原理把source对象的属性赋值给target对象,所以如果你改了字段idea不会提示copy的地方报错需要修改,而是运行才报错。同时要注意的是BeanUtils并不会对null进行处理,而是会将其作为属性值直接赋值给target。如果转换的属性是对象,那这个对象转换的过程中会浅克隆,达成共享。BeanUtils也对其获取对象做了优化,通过对其底层代码的查找,BeanUtils有一个getPropertyDescriptors方法获取所有属性的方法,再往下就会发现获取属性的时候先从强缓存中查找是否有当前类的属性集合,没有就去弱缓存中查找。如果还都没有,才会进行创建创建,因此大大提升了性能。
另外一个使用反射的工具就是Apache的BeanUtils,其性能要比spring的BeanUtils低很多。在使用上Spring和Apache的copy属性的方法源和目的参数得位置正好相反,所以导包和调用得时候需要注意一下。
MapStruct
MapStruct是一种代码生成器,底层没有使用反射而是使用get、set做的数据转换,它极大地简化了基于"约定优于配置"方法的Java bean类型之间映射的实现。生成的映射代码使用纯方法调用,因此快速、类型安全且易于理解。在使用时,要先导入maven,同时在maven中添加如下配置:
为了让上述的配置生效还要在maven-compiler-plugin插件中配置上对mapstruct-processor的引用。最后我们封装一个数据转换的映射接口,使用接口@Mapper和方法@Mappings(source = "数据1", target = "数据2")注解。同时MapStruct会对基本类型、封装类型、String类型和枚举类型之间自动做映射,不需要我们做额外配置。如果我们在转换映射过程中,想要给一些属性定义一个固定的值,这个时候可以使用@Mapping(source = "数据", constant = "具体值")。除了以上的类型,MapStruct做类型转换会使用@Mapping(target = "数据2",expression = "java(具体转换类型)"),相比较于其他几种工具,因为MapStruct在编译时生成bean映射,确保了高性能,所以当我们在做高并发、成千上百万的数据交互的时候,会优先使用MapStruct。有的人会觉得操作麻烦,每当有新的数据的时候,都要封装一个新的交互接口,开头我们说到:计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决,如果不行,那就加两层。我们也可以对映射的接口抽取再封装,解决操作麻烦的问题。MapStruct的官方例子把接口定义成 EntityMapper, DtoMapper,不用每种映射都要单独定义映射接口。
Orika
数据转换的Java Bean映射框架除了MapStruct还有Orika,他将数据从一个对象递归复制到另一个对象,底层采用了javassist类库生成Bean映射的字节码,之后调用就直接加载执行生成的字节码文件。所以在开发多层应用程序时,她非常有用。使用的话先导入maven:然后构造一个spring集成的MapperFactory:
接下来在构造的MapperFactory里注册字段映射:
字段映射是双向的,我们可以从目标类型映射回源类型,byDefault()方法用于注册名称相同的属性(如果所有属性名称都相同则可以省略上面步骤),如果不希望某个字段参与映射,可以使用exclude方法最后就是进行映射:
如果是复杂的映射直接在注册字段映射的field方法里映射。同时orika支持自定义转换器,将指定类型或指定名称的属性做映射时添加自定义操作。
总结
以上介绍的数据转换都是BeanCopy框架中的一种,BeanCopy框架一共有Dozer、BeanCopier、BeanUtils、MapStruct、ModelMapper、Selma、Orika、JMapper这几种。其原理就是下图:如果就性能来说的话,如下图所示:
以上所说都是内存内的数据交互!