|
这是第338篇不掺水的,想要了解更多,请戳下方卡片关注我们吧~BeanCopy问题我们复制对象最常用的方法是使用 BeanCopy 工具类,这是一种常见的 DTO 对象复制方法。然而,BeanCopy 在处理复杂继承和嵌套类型时常常出现问题,导致开发人员需要花费大量时间来手动处理 DTO 对象之间的映射关系。无法处理继承关系:Bean Copy 不能正确地处理继承关系,如果源对象和目标对象之间存在继承关系,Bean Copy 可能会复制不正确的属性或出现运行时错误。递归复制问题:Bean Copy 可能会导致递归复制的问题,例如A对象中包含B对象,而B对象中又包含A对象的引用,这种情况下,Bean Copy 可能会导致无限递归的问题。不支持复杂类型:Bean Copy 通常只能复制简单类型的 Bean ,如果需要复制的 Bean 中包含嵌套的复杂类型,需要实现自定义的转换处理。性能问题:Bean Copy 是基于反射实现的,因此在复制大量对象时可能会存在性能问题,影响系统的响应速度和性能。对象赋值黑盒,当业务变的复杂,对象层层转换,很难找到属性是在哪里赋值,后期扩展及排查问题埋下隐患。举个例子:假设您有一个源对象 User 和一个目标对象 UserDTO ,它们的属性如下所示:publicclassUser{privateLongid;privateStringgivenName;privateStringemail;privateListroles;//constructor,gettersandsetters}publicclassUserDto{privateLongid;privateStringfirstName;privateStringemail;privateListroles;//constructor,gettersandsetters}如果要将一个 User 对象拷贝到另一个 UserDTO 对象中,可以使用 Bean Copy 的方式,如下:UsersourceUser=newUser();sourceUser.setId(1L);sourceUser.setGivenName("John");sourceUser.setEmail("johndoe@example.com");sourceUser.setRoles(roles);UserDTOtargetUserDTO=newUserDTO();BeanUtils.copyProperties(sourceUser,targetUserDTO);System.out.println(targetUserDTO.getFirstName());//Output:null但是,使用 Bean Copy 的方式会造成以下问题:属性名不同无法赋值,因为源对象的 givenName 属性与目标对象的 firstName 属性名不同,需要编写自定义的映射逻辑。浅拷贝:使用 Bean Copy 的方式,目标对象拷贝的是源对象的地址。如果 User 对象的 role 改动,UserDTO 对象中的 role 也回会随着一起改变使用MapStruct改进MapStruct 是一个代码生成器,它可以自动生成映射器代码,可以解决 BeanCopy 存在的问题。接下来,我们来看看如何使用 MapStruct 来解决这个问题。需要先定义一个 Mapper 接口,代码如下:@MapperpublicinterfaceUserConverter{UserConverterINSTANCE=Mappers.getMapper(UserConverter.class);@Mapping(target="firstName",source="givenName")UserDTOtoDto(Useruser);ListtoDtoList(ListuserList);}在上面的例子中,使用注解 @Mapper 定义了一个接口 UserConveter,该接口编译时会由 MapStruct 动态生成实现类,使用该实现类进行 Java Bean 对象的拷贝。并且使用 @Mapping 注解指定了 User 对象的 givenName 和 UserDTO 对象 firstName 的映射关系。使用 MapStruct 进行 User 对象拷贝:UserDTOdto=UserConverter.INSTANCE.toDto(user);MapStruct一些基本用法使用 MapStruct 的主要优点如下:简化配置:使用 MapStruct 可以简化 Java Bean 对象拷贝的配置,避免了使用 Bean Copy 时出现的配置问题;提高效率:MapStruct 在编译时会自动将 Java Bean 对象拷贝的代码进行优化,提高了拷贝的效率;支持复杂类型:MapStruct 支持复杂 Java Bean 对象之间的属性拷贝,包括集合、继承、多态等;易于维护:使用 MapStruct 可以使代码更加清晰和易于维护。下面列一下 MapStruct 的常见的一些用法使用 @Mapper 注解时,添加 componentModel = "spring" 参数可以指定使用 Spring 作为注入依赖的框架,这样就可以在我们业务 Service 中用 @Autowired 注解引入当作 Bean 来使用。@Mapper(componentModel="spring")publicinterfaceUserConverter{//...}使用@AutowiredprivateUserConverteruserConverter;//转化UserDTOuserDTO=userConverter.toDto(user);可以支持自定义字段映射,只需要在方法签名上,使用 @Mapping 注解,并指明需要转换的源对象的名字和目标对象的名字就可以了,并且支持多层级对象。 使用 @InheritInverseConfiguration 注解来自动生成反向映射方法,避免手动编写反向映射方法。publicinterfaceEmployeeMapper{@Mapping(target="name",source="person.name")@Mapping(target="age",source="person.age")@Mapping(target="employeeId",source="employeeId")EmployeeDTOemployeeToEmployeeDTO(Employeeemployee);@InheritInverseConfigurationEmployeeemployeeDTOToEmployee(EmployeeDTOemployeeDTO);}使用 @Mapping 注解时,可以添加 expression 参数来指定自定义映射逻辑,例如计算、格式化等。publicinterfaceEmployeeMapper{@Mapping(target="age",expression="java(LocalDate.now().getYear()-employee.getBirthDate().getYear())")EmployeeDTOemployeeToEmployeeDTO(Employeeemployee);}@AfterMapping 注解可以被应用于映射的方法,这样在调用映射方法之后,自动执行标注了 @AfterMapping注解的方法。将 EpPlanApply 对象中逗号分隔的字符串 auditUserIds 转化为 PlanApply 中的 SetauditUserIds:PlanApplypoToDomain(EpPlanApplyplanApply);@AfterMappingdefaultvoidafterPoToDomain(@MappingTargetPlanApplyplanApply,EpPlanApplyepPlanApply){StringauditUserIdsStr=epPlanApply.getAuditUserIds();if(StringUtils.isNotBlank(auditUserIdsStr)){SetauditUserIds=newHashSet();for(Stringsplit:auditUserIdsStr.split(",")){auditUserIds.add(Long.valueOf(split));}planApply.setAuditUserIds(auditUserIds);}}总结Bean Copy 和 MapStruct 都是 Java 中用于对象之间复制属性的工具,它们可以大大简化代码的编写和维护。MapStruct 的性能更高,因为它在编译时生成代码,而 Bean Copy 需要使用反射机制获取对象的属性信息。但是MapStruct 的学习成本相对较高,因为需要了解注解的使用方法以及如何配置和生成代码。BeanCopy 适用于简单的属性复制场景,而 MapStruct 则适用于复杂的属性复制场景,尤其是在需要频繁进行属性复制的情况下,使用 MapStruct 可以提高代码的执行效率。选择使用哪种工具取决于具体的业务需求和开发团队的技术水平。看完两件事如果你觉得这篇内容对你挺有启发,我想邀请你帮我两件小事1.点个「在看」,让更多人也能看到这篇内容(点了「在看」,bug -1 )2.关注公众号「政采云技术」,持续为你推送精选好文招贤纳士政采云技术团队(Zero),Base 杭州,一个富有激情和技术匠心精神的成长型团队。规模 500 人左右,在日常业务开发之外,还分别在云原生、区块链、人工智能、低代码平台、中间件、大数据、物料体系、工程平台、性能体验、可视化等领域进行技术探索和实践,推动并落地了一系列的内部技术产品,持续探索技术的新边界。此外,团队还纷纷投身社区建设,目前已经是 google flutter、scikit-learn、Apache Dubbo、Apache Rocketmq、Apache Pulsar、CNCF Dapr、Apache DolphinScheduler、alibaba Seata 等众多优秀开源社区的贡献者。如果你想改变一直被事折腾,希望开始折腾事;如果你想改变一直被告诫需要多些想法,却无从破局;如果你想改变你有能力去做成那个结果,却不需要你;如果你想改变你想做成的事需要一个团队去支撑,但没你带人的位置;如果你想改变本来悟性不错,但总是有那一层窗户纸的模糊……如果你相信相信的力量,相信平凡人能成就非凡事,相信能遇到更好的自己。如果你希望参与到随着业务腾飞的过程,亲手推动一个有着深入的业务理解、完善的技术体系、技术创造价值、影响力外溢的技术团队的成长过程,我觉得我们该聊聊。任何时间,等着你写点什么,发给 zcy-tc@cai-inc.com
|
|