MapStruct 对象映射最佳实践
Alex Chen | 2026-09-01T08:56:45 | Java, Spring Boot
全面介绍 MapStruct 的注解驱动映射、嵌套对象转换、自定义转换器、与 Spring 集成,以及常见踩坑汇总。
# MapStruct 对象映射最佳实践 ## 为什么选择 MapStruct 相比 BeanUtils.copyProperties 的反射方式,MapStruct 在编译期生成映射代码,性能接近手写 getter/setter,且类型安全、易于调试。 ## 基础映射 ```java @Mapper(componentModel = "spring") public interface UserMapper { @Mapping(source = "name", target = "fullName") @Mapping(source = "createTime", target = "registeredAt", dateFormat = "yyyy-MM-dd") @Mapping(target = "id", ignore = true) UserDTO toDTO(User user); @Mapping(source = "fullName", target = "name") User toEntity(UserDTO dto); List toDTOList(List users); } ``` ## 嵌套对象映射 ```java @Mapper(componentModel = "spring", uses = {AddressMapper.class}) public interface OrderMapper { @Mapping(source = "customer.name", target = "customerName") @Mapping(source = "customer.address", target = "shippingAddress") @Mapping(source = "items", target = "orderItems") OrderDTO toDTO(Order order); } @Mapper(componentModel = "spring") public interface AddressMapper { AddressDTO toDTO(Address address); } ``` ## 自定义转换逻辑 ```java @Mapper(componentModel = "spring") public abstract class ProductMapper { @Mapping(target = "discountedPrice", source = "product", qualifiedByName = "calcDiscount") public abstract ProductVO toVO(Product product); @Named("calcDiscount") protected BigDecimal calculateDiscount(Product product) { if (product.getDiscount() == null) { return product.getPrice(); } return product.getPrice() .multiply(BigDecimal.ONE.subtract(product.getDiscount())); } } ``` ## 更新已有对象 ```java @Mapper(componentModel = "spring", nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE) public interface UserUpdateMapper { @Mapping(target = "id", ignore = true) @Mapping(target = "createTime", ignore = true) void updateEntity(UserUpdateDTO dto, @MappingTarget User user); } ``` MapStruct 的 `@MappingTarget` 特别适合 PATCH 更新场景,配合 `IGNORE` 策略可以跳过 null 字段,避免覆盖已有数据。编译后生成的代码可以直接在 target 目录查看,方便排查映射问题。