Why Not to Use Spring BeanUtils for Object Copying and How MapperStruct Improves Performance
Spring’s BeanUtils.copyProperties is error‑prone and slow due to type mismatches, primitive‑wrapper issues, null overwrites and reflection, whereas MapStruct generates compile‑time, type‑safe mapper code that avoids these pitfalls, delivers far better performance, and integrates easily with Maven and Lombok.
In Java business development, copying properties between BO, PO, DTO objects often uses Spring's BeanUtils.copyProperties , but it has several drawbacks.
Inconsistent property types cause copy failure : e.g., Long vs String leads to null values.
Primitive vs wrapper mismatch : Boolean fields with is -prefixed getters may fail.
Null values overwrite existing data : copying a DTO with null fields overwrites non‑null fields in the target.
Reflection‑based implementation is slow .
Incorrect imports can cause runtime errors .
Note: If a boolean property uses both primitive and wrapper types and its name starts with is , BeanUtils may fail to copy it.
MapperStruct addresses these issues. It generates type‑safe, pre‑compiled mapping code, eliminating reflection overhead and providing much higher performance than BeanUtils.
Example of generated mapper implementation (simplified):
@Generated(value = "org.mapstruct.ap.MappingProcessor", date = "2024-02-07T00:50:05+0800", comments = "version: 1.4.2.Final, compiler: javac, environment: Java 1.8.0_211 (Oracle Corporation)")
public class XXXDTOConverterImpl implements XXXDTOConverter {
@Override
public XXXBO convertDtoToBo(XXXDTO xxxDTO) {
if (xxxDTO == null) {
return null;
}
XXXBO xxxBO = new XXXBO();
xxxBO.setId(xxxDTO.getId());
xxxBO.setLabelName(xxxDTO.getLabelName());
xxxBO.setSortNum(xxxDTO.getSortNum());
xxxBO.setCategoryId(xxxDTO.getCategoryId());
return xxxBO;
}
}To use MapperStruct, add Maven dependencies:
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.4.2.Final</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.4.2.Final</version>
</dependency>Define DTO and BO classes, then create a mapper interface:
@Mapper
public interface SubjectLabelDTOConverter {
SubjectLabelDTOConverter INSTANCE = Mappers.getMapper(SubjectLabelDTOConverter.class);
SubjectLabelBO convertDtoToBo(SubjectLabelDTO subjectLabelDTO);
}Usage example:
public Result
add(SubjectLabelDTO subjectLabelDTO) {
SubjectLabelBO subjectLabelBO = SubjectLabelDTOConverter.INSTANCE.convertDtoToBo(subjectLabelDTO);
// ...
}Note: MapperStruct also performs shallow copy; deep copy requires additional converters. When used together with Lombok, ensure Lombok dependency is declared after MapStruct to avoid null‑value issues.
Java Tech Enthusiast
Sharing computer programming language knowledge, focusing on Java fundamentals, data structures, related tools, Spring Cloud, IntelliJ IDEA... Book giveaways, red‑packet rewards and other perks await!
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.