mcpField
M
mapstruct-patterns

mapstruct-patterns

github.com/full-stack-skillsDevelopment7101 installsUpdated 5w ago
Repository

About mapstruct-patterns

mapstruct-patterns is a MapStruct practice guide for Java developers. It covers mapper design, Spring injection, nested mappings, qualifiers, and common configuration pitfalls.

Key features

  1. 01Mapper definition options
  2. 02Spring injection strategies
  3. 03Reusable nested mappings
  4. 04Choosing @Qualifier or @Named
  5. 05Lombok processor ordering

Use cases

  1. 01Designing mappers with custom logic
  2. 02Handling Spring mapper circular dependencies
  3. 03Structuring reusable nested field mappings

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/full-stack-skills/java-skills --skill mapstruct-patterns

After installing, type/mapstruct-patternsin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

agents/
openai.yaml
examples/
mapstruct-realworld.md
references/
mapstruct-resources.md

Details

From the project README

MapStruct 最佳实践模式

来源: MapStruct Reference Guide | GitHub: mapstruct/mapstruct

Capability Boundaries

✅ Strong Suits
  1. Mapper 三种定义方式 — 接口(标准) / 抽象类(需要字段) / default方法(自定义逻辑)
  2. Spring 注入策略 — CONSTRUCTOR(推荐,测试友好) / SETTER(解决循环依赖)
  3. 嵌套映射模式 — 显式子方法封装(推荐) > dot notation 一次性配置
  4. @Qualifier vs @Named — 自定义 @Qualifier 注解(IDE重构安全) > @Named(字符串不安全)
  5. Lombok 集成 — annotationProcessorPaths 中 Lombok 必须在 MapStruct 之前
  6. @MappingComposition — 组合注解复用,但 error messages 不成熟需谨慎
❌ Out of Scope
  1. 运行时动态 Bean 映射(Orika/Dozer) → MapStruct 编译期生成,不是反射

核心模式

模式 1: Spring 注入策略选择
// ✅ 推荐:CONSTRUCTOR 注入(测试友好,不依赖Spring容器)
@Mapper(componentModel = "spring", injectionStrategy = InjectionStrategy.CONSTRUCTOR)
public interface UserMapper { UserVO toVO(User e); }

// ✅ 循环依赖:SETTER注入
@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);               // MapStruct 自动检测嵌套类型
    AddressDTO toAddressDTO(Address e);   // 子映射方法 — 唯一配置处
}

// ✅ dot notation + 子方法组合(局部覆盖)
@Mapping(target = "address.zip", constant = "00000")  // 常量覆盖
@Mapping(target = "address", source = "homeAddr")     // 其余走子方法
UserDTO toDTO(User e);
模式 3: @Qualifier > @Named
// ✅ 推荐:自定义 @Qualifier(IDE重构安全)
@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(字符串依赖,IDE不能自动重命名)
@Named("toUpper") default String toUpper(String s) { return s.toUpperCase(); }
@Mapping(target = "name", qualifiedByName = "toUpper")  // 字符串引用
模式 4: 循环依赖的 Spring 配置
// 场景: UserMapper → OrderMapper → UserMapper
@Mapper(componentModel = "spring", injectionStrategy = InjectionStrategy.SETTER,
        uses = { OrderMapper.class })  // ← SETTER是关键
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

  1. Lombok 的 annotationProcessor 必须在 MapStruct 之前 — 否则编译期找不到 getter/setter
  2. CONSTRUCTOR 注入遇循环依赖会编译失败 — 改为 SETTER
  3. dot notation 不创建子映射方法 — 嵌套字段逻辑重复时用显式子方法
  4. MapStruct 编译期生成,性能等同手写代码 — 不是反射
  5. @Named 字符串引用不安全 — IDE重构不会自动更新,用自定义 @Qualifier
  6. CollectionMappingStrategy.DEFAULT 不要显式使用 — 仅作内部区分
  7. Mapper 接口中 default 方法优先于生成代码 — 可用于自定义转换逻辑

Data Privacy

本技能不收集、存储或传输任何用户数据。

Comments

Markdown supported