5.对象赋值黑盒,当业务变的复杂,对象层层转换,很难找到属性是在哪里赋值,后期扩展及排查问题埋下隐患。
public class User { // 堆代码 duidaima.com private Long id; private String givenName; private String email; private List<Role> roles; // constructor, getters and setters } public class UserDto { private Long id; private String firstName; private String email; private List<Role> 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 的方式会造成以下问题:
浅拷贝:使用 Bean Copy 的方式,目标对象拷贝的是源对象的地址。如果 User 对象的 role 改动,UserDTO 对象中的 role 也回会随着一起改变
@Mapper public interface UserConverter { UserConverter INSTANCE = Mappers.getMapper(UserConverter.class); @Mapping(target = "firstName", source = "givenName") UserDTO toDto(User user); List<UserDTO> toDtoList(List<User> userList); }在上面的例子中,使用注解 @Mapper 定义了一个接口 UserConveter,该接口编译时会由 MapStruct 动态生成实现类,使用该实现类进行 Java Bean 对象的拷贝。并且使用 @Mapping 注解指定了 User 对象的 givenName 和 UserDTO 对象 firstName 的映射关系。
UserDTO dto = UserConverter.INSTANCE.toDto(user);
易于维护:使用 MapStruct 可以使代码更加清晰和易于维护。
@Mapper(componentModel = "spring") public interface UserConverter { // ... }
使用 @Autowired private 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); }3.使用 @Mapping 注解时,可以添加 expression 参数来指定自定义映射逻辑,例如计算、格式化等。
public interface EmployeeMapper { @Mapping(target = "age", expression = "java(LocalDate.now().getYear() - employee.getBirthDate().getYear())") EmployeeDTO employeeToEmployeeDTO(Employee employee); }4. @AfterMapping 注解可以被应用于映射的方法,这样在调用映射方法之后,自动执行标注了 @AfterMapping注解的方法。
PlanApply poToDomain(EpPlanApply planApply); @AfterMapping default void afterPoToDomain(@MappingTarget PlanApply planApply, EpPlanApply epPlanApply) { String auditUserIdsStr = epPlanApply.getAuditUserIds(); if (StringUtils.isNotBlank(auditUserIdsStr)) { Set<Long> auditUserIds = new HashSet<>(); for (String split : auditUserIdsStr.split(",")) { auditUserIds.add(Long.valueOf(split)); } planApply.setAuditUserIds(auditUserIds); } }