Problems with BeanCopy and Improving Object Mapping Using MapStruct
This article examines the limitations of BeanCopy for Java DTO mapping—such as inheritance handling, recursive copying, lack of complex type support, performance overhead, and opaque assignments—and demonstrates how MapStruct can address these issues with compile‑time generated, type‑safe mappers.
BeanCopy is a commonly used utility for copying Java objects, but it struggles with inheritance, recursive references, complex nested types, performance due to reflection, and opaque property assignments, especially in large or evolving codebases.
For example, copying a User object to a UserDto using BeanUtils.copyProperties fails to map givenName to firstName and performs a shallow copy of collections, leading to unexpected side effects.
public class User {
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 sourceUser = new User();
sourceUser.setId(1L);
sourceUser.setGivenName("John");
sourceUser.setEmail("[email protected]");
sourceUser.setRoles(roles);
UserDto targetUserDTO = new UserDto();
BeanUtils.copyProperties(sourceUser, targetUserDTO);
System.out.println(targetUserDTO.getFirstName()); // Output: nullMapStruct solves these problems by generating mapper implementations at compile time. By defining a mapper interface with @Mapper and specifying field mappings with @Mapping , developers obtain type‑safe, performant code without reflection.
@Mapper
public interface UserConverter {
UserConverter INSTANCE = Mappers.getMapper(UserConverter.class);
@Mapping(target = "firstName", source = "givenName")
UserDto toDto(User user);
List
toDtoList(List
userList);
}Using the generated mapper is straightforward:
UserDto dto = UserConverter.INSTANCE.toDto(user);MapStruct also supports Spring integration, custom field mappings, inverse configurations, expression‑based mappings, and lifecycle callbacks such as @AfterMapping for post‑processing.
@Mapper(componentModel = "spring")
public interface UserConverter {
// ...
}
@Autowired
private UserConverter userConverter;
UserDto userDTO = userConverter.toDto(user); 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);
} public interface EmployeeMapper {
@Mapping(target = "age", expression = "java(LocalDate.now().getYear() - employee.getBirthDate().getYear())")
EmployeeDTO employeeToEmployeeDTO(Employee employee);
} PlanApply poToDomain(EpPlanApply planApply);
@AfterMapping
default void afterPoToDomain(@MappingTarget PlanApply 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);
}
}In summary, both BeanCopy and MapStruct simplify object property copying, but MapStruct offers higher performance through compile‑time code generation and better support for complex mappings, while BeanCopy remains suitable for simple, quick copies.
Choosing between them depends on the project's complexity, performance requirements, and the development team's familiarity with annotation‑based code generation.
政采云技术
ZCY Technology Team (Zero), based in Hangzhou, is a growth-oriented team passionate about technology and craftsmanship. With around 500 members, we are building comprehensive engineering, project management, and talent development systems. We are committed to innovation and creating a cloud service ecosystem for government and enterprise procurement. We look forward to your joining us.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.