| name | mapstruct-patterns |
| description | MapStruct 最佳实践模式。从官方 Reference Guide 提炼,覆盖 Mapper 三种定义方式(接口/抽象类/default方法)、Spring 注入策略(CONSTRUCTOR推荐/SETTER解决循环依赖)、嵌套映射模式(显式子方法 > dot notation 一次性配置)、@Qualifier > @Named 的安全选择、Lombok 集成规则、@MappingComposition 谨慎使用。
纠正 LLM 最常见的错误:用反射 BeanUtils 而非编译期 MapStruct、嵌套映射全写在一个方法、不知 SETTER 解决循环依赖。
|
| license | Apache-2.0 |
MapStruct 最佳实践模式
来源: MapStruct Reference Guide | GitHub: mapstruct/mapstruct
Capability Boundaries
✅ Strong Suits
- Mapper 三种定义方式 — 接口(标准) / 抽象类(需要字段) / default方法(自定义逻辑)
- Spring 注入策略 — CONSTRUCTOR(推荐,测试友好) / SETTER(解决循环依赖)
- 嵌套映射模式 — 显式子方法封装(推荐) > dot notation 一次性配置
- @Qualifier vs @Named — 自定义 @Qualifier 注解(IDE重构安全) > @Named(字符串不安全)
- Lombok 集成 — annotationProcessorPaths 中 Lombok 必须在 MapStruct 之前
- @MappingComposition — 组合注解复用,但 error messages 不成熟需谨慎
❌ Out of Scope
- 运行时动态 Bean 映射(Orika/Dozer) → MapStruct 编译期生成,不是反射
核心模式
模式 1: Spring 注入策略选择
@Mapper(componentModel = "spring", injectionStrategy = InjectionStrategy.CONSTRUCTOR)
public interface UserMapper { UserVO toVO(User e); }
@Mapper(componentModel = "spring", injectionStrategy = InjectionStrategy.SETTER,
uses = { OrderMapper.class })
public interface UserMapper { ... }
@MapperConfig(componentModel = "spring", injectionStrategy = InjectionStrategy.CONSTRUCTOR)
public interface CentralConfig {}
@Mapper(config = CentralConfig.class) public interface UserMapper { ... }
模式 2: 嵌套映射的正确方式
@Mapping(target = "address.street", source = "home.street")
@Mapping(target = "address.city", source = "home.city")
UserDTO toDTO(User e);
@Mapper
public interface UserMapper {
UserDTO toDTO(User e);
AddressDTO toAddressDTO(Address e);
}
@Mapping(target = "address.zip", constant = "00000")
@Mapping(target = "address", source = "homeAddr")
UserDTO toDTO(User e);
模式 3: @Qualifier > @Named
@Qualifier @Target(ElementType.METHOD) @Retention(RetentionPolicy.CLASS)
public @interface ToUpperCase {}
@ToUpperCase default String toUpper(String s) { return s.toUpperCase(); }
@Mapping(target = "name", qualifiedBy = ToUpperCase.class)
UserDTO toDTO(User e);
@Named("toUpper") default String toUpper(String s) { return s.toUpperCase(); }
@Mapping(target = "name", qualifiedByName = "toUpper")
模式 4: 循环依赖的 Spring 配置
@Mapper(componentModel = "spring", injectionStrategy = InjectionStrategy.SETTER,
uses = { OrderMapper.class })
public interface UserMapper {
@Mapping(target = "orders", source = "orderList")
UserDTO toDTO(User entity);
}
@Mapper(componentModel = "spring", injectionStrategy = InjectionStrategy.SETTER,
uses = { UserMapper.class })
public interface OrderMapper { ... }
模式 5: 抽象类 Mapper
@Mapper
public abstract class UserMapper {
@Autowired protected PasswordEncoder encoder;
@Mapping(target = "encodedPwd", expression = "java(encoder.encode(dto.getPassword()))")
public abstract UserVO toVO(UserDTO dto);
protected String formatDate(LocalDateTime dt) { return dt.format(FORMATTER); }
}
Gotchas
- Lombok 的 annotationProcessor 必须在 MapStruct 之前 — 否则编译期找不到 getter/setter
- CONSTRUCTOR 注入遇循环依赖会编译失败 — 改为 SETTER
- dot notation 不创建子映射方法 — 嵌套字段逻辑重复时用显式子方法
- MapStruct 编译期生成,性能等同手写代码 — 不是反射
- @Named 字符串引用不安全 — IDE重构不会自动更新,用自定义 @Qualifier
- CollectionMappingStrategy.DEFAULT 不要显式使用 — 仅作内部区分
- Mapper 接口中 default 方法优先于生成代码 — 可用于自定义转换逻辑
Data Privacy
本技能不收集、存储或传输任何用户数据。