找回密码
 会员注册
查看: 20|回复: 0

BeanCopy坑到MapStruct技巧

[复制链接]

2万

主题

0

回帖

6万

积分

超级版主

积分
64101
发表于 2024-10-12 19:56:40 | 显示全部楼层 |阅读模式
BeanCopy坑到MapStruct技巧 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 ,它们的属性如下所示:public class User {  private Long id;  private String givenName;  private String email;  private List roles;   // constructor, getters and setters}public class UserDto {  private Long id;  private String firstName;  private String email;  private List roles;   // constructor, getters and setters}如果要将一个 User 对象拷贝到另一个 UserDTO 对象中,可以使用 Bean Copy 的方式,如下:User sourceUser = new User();sourceUser.setId(1L);sourceUser.setGivenName("John");sourceUser.setEmail("johndoe@example.com");sourceUser.setRoles(roles);UserDTO targetUserDTO = new UserDTO();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 接口,代码如下:@Mapperpublic interface UserConverter {      UserConverter INSTANCE = Mappers.getMapper(UserConverter.class);         @Mapping(target = "firstName", source = "givenName")    UserDTO toDto(User user);      List toDtoList(List userList);}在上面的例子中,使用注解 @Mapper 定义了一个接口 UserConveter,该接口编译时会由 MapStruct 动态生成实现类,使用该实现类进行 Java Bean 对象的拷贝。并且使用 @Mapping 注解指定了 User 对象的 givenName 和 UserDTO 对象 firstName 的映射关系。使用 MapStruct 进行 User 对象拷贝:UserDTO dto = 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")public interface UserConverter {// ...}使用@Autowiredprivate UserConverter userConverter; // 转化UserDTO userDTO = userConverter.toDto(user);可以支持自定义字段映射,只需要在方法签名上,使用 @Mapping 注解,并指明需要转换的源对象的名字和目标对象的名字就可以了,并且支持多层级对象。 使用 @InheritInverseConfiguration 注解来自动生成反向映射方法,避免手动编写反向映射方法。public interface EmployeeMapper {    @Mapping(target = "name", source = "person.name")    @Mapping(target = "age", source = "person.age")    @Mapping(target = "employeeId", source = "employeeId")    EmployeeDTO employeeToEmployeeDTO(Employee employee);     @InheritInverseConfiguration    Employee employeeDTOToEmployee(EmployeeDTO employeeDTO);}使用 @Mapping 注解时,可以添加 expression 参数来指定自定义映射逻辑,例如计算、格式化等。public interface EmployeeMapper { @Mapping(target = "age", expression = "java(LocalDate.now().getYear() - employee.getBirthDate().getYear())") EmployeeDTO employeeToEmployeeDTO(Employee employee);}@AfterMapping 注解可以被应用于映射的方法,这样在调用映射方法之后,自动执行标注了 @AfterMapping注解的方法。将 EpPlanApply 对象中逗号分隔的字符串 auditUserIds 转化为 PlanApply 中的 Set auditUserIds:PlanApply poToDomain(EpPlanApply planApply);  @AfterMappingdefault void afterPoToDomain(@MappingTarget lanApply planApply, EpPlanApply epPlanApply) {       String auditUserIdsStr = epPlanApply.getAuditUserIds();       if (StringUtils.isNotBlank(auditUserIdsStr)) {            Set auditUserIds = new HashSet();            for (String split : auditUserIdsStr.split(",")) {                auditUserIds.add(Long.valueOf(split));            }            planApply.setAuditUserIds(auditUserIds);       }}总结Bean Copy 和 MapStruct 都是 Java 中用于对象之间复制属性的工具,它们可以大大简化代码的编写和维护。MapStruct 的性能更高,因为它在编译时生成代码,而 Bean Copy 需要使用反射机制获取对象的属性信息。但是MapStruct 的学习成本相对较高,因为需要了解注解的使用方法以及如何配置和生成代码。BeanCopy 适用于简单的属性复制场景,而 MapStruct 则适用于复杂的属性复制场景,尤其是在需要频繁进行属性复制的情况下,使用 MapStruct 可以提高代码的执行效率。选择使用哪种工具取决于具体的业务需求和开发团队的技术水平。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 会员注册

本版积分规则

QQ|手机版|心飞设计-版权所有:微度网络信息技术服务中心 ( 鲁ICP备17032091号-12 )|网站地图

GMT+8, 2024-12-26 12:21 , Processed in 0.671560 second(s), 25 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表