# Mapstruct Plus

> Java 对象映射转换框架 MapStruct Plus 使用指南。覆盖 DTO/Entity/VO 转换、@AutoMapper 注解、枚举/Map/循环引用等场景。当用户提到 BeanUtils.copyProperties、ModelMapper、对象拷贝、DTO 转换时也应激活。

- Skill: `liangxp1990/mapstruct-plus` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add liangxp1990/mapstruct-plus`
- Raw SKILL.md: https://api.skillmd.com/api/skills/liangxp1990/mapstruct-plus/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: liangxp1990 (https://skillmd.com/u/liangxp1990)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/liangxp1990/mapstruct-plus

---


# MapStruct Plus 使用指南

MapStruct Plus 是 MapStruct 的增强工具，基于 JSR 269 注解处理器，通过注解自动生成 Mapper 接口。
核心优势：只需在类上加一个 `@AutoMapper` 注解，即可自动生成双向转换代码，无需手写 Mapper 接口。

> **触发条件**：当用户在 Java/Spring Boot 项目中需要以下操作时，请主动激活此 skill：
> - 在两个 Java 类之间进行对象转换（如 DTO <-> Entity, VO <-> DTO）
> - 使用 @AutoMapper、@AutoMapping、@AutoMappers 等注解
> - 配置 mapstruct-plus 依赖和注解处理器
> - 使用 Converter 进行对象转换操作
> - 处理枚举转换、Map 转对象、循环引用等场景
> - 解决 mapstruct-plus 相关的编译或运行时问题
> - 提到 BeanUtils.copyProperties、ModelMapper、对象拷贝、类型转换、DTO 转换等关键词

> **重要**：MapStruct Plus 已内嵌 MapStruct，不要再引入 MapStruct 依赖，避免版本冲突。

## 快速上手

### 1. 添加依赖

**SpringBoot 环境**（推荐）:

```xml
<properties>
    <mapstruct-plus.version>最新版本</mapstruct-plus.version>
</properties>
<dependencies>
    <dependency>
        <groupId>io.github.linpeilie</groupId>
        <artifactId>mapstruct-plus-spring-boot-starter</artifactId>
        <version>${mapstruct-plus.version}</version>
    </dependency>
</dependencies>
```

**注解处理器配置**（maven-compiler-plugin 中必须添加）:

```xml
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.8.1</version>
    <configuration>
        <annotationProcessorPaths>
            <path>
                <groupId>io.github.linpeilie</groupId>
                <artifactId>mapstruct-plus-processor</artifactId>
                <version>${mapstruct-plus.version}</version>
            </path>
        </annotationProcessorPaths>
    </configuration>
</plugin>
```

**与 Lombok 配合**（lombok >= 1.18.16）, 需额外添加 `lombok-mapstruct-binding`:

```xml
<annotationProcessorPaths>
    <path>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>${lombok.version}</version>
    </path>
    <path>
        <groupId>io.github.linpeilie</groupId>
        <artifactId>mapstruct-plus-processor</artifactId>
        <version>${mapstruct-plus.version}</version>
    </path>
    <path>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok-mapstruct-binding</artifactId>
        <version>0.2.0</version>
    </path>
</annotationProcessorPaths>
```

**非 SpringBoot 环境**: 依赖改为 `mapstruct-plus`（不是 starter），添加 `@ComponentModelConfig(componentModel = "default")` 配置类，使用 `new Converter()` 创建实例。

### 2. 声明映射关系

在任意一个类上加 `@AutoMapper`:

```java
@AutoMapper(target = UserDto.class)
@Data
public class User {
    private String username;
    private int age;
}
```

### 3. 执行转换

```java
// SpringBoot - 自动注入
@Autowired
private Converter converter;

UserDto dto = converter.convert(user, UserDto.class);
List<UserDto> dtoList = converter.convert(userList, UserDto.class);
User updated = converter.convert(dto, existingUser);  // 更新已有对象
```

## 核心注解速查

| 注解 | 位置 | 用途 | 核心属性 |
|------|------|------|----------|
| `@AutoMapper` | 类 | 声明转换关系 | `target`(必填), `uses`, `reverseConvertGenerate`, `cycleAvoiding` |
| `@AutoMappers` | 类 | 多目标转换 | 包含多个 `@AutoMapper` |
| `@AutoMapping` | 字段 | 自定义属性映射 | `target`, `source`, `dateFormat`, `numberFormat`, `ignore`, `expression`, `qualifiedByName` |
| `@AutoMappings` | 字段 | 多映射规则 | 包含多个 `@AutoMapping`, 用 `targetClass` 区分 |
| `@AutoEnumMapper` | 枚举类 | 枚举自动转换 | `value`(枚举唯一标识字段名) |
| `@AutoMapMapper` | 类 | Map转对象 | 无属性 |
| `@ReverseAutoMapping` | 字段 | 反向映射配置 | `source`(目标类属性), `target`(源类属性) |
| `@MapperConfig` | 类(全局) | 模块配置 | `mapperPackage`, `unmappedTargetPolicy`, `nullValueMappingStrategy` |
| `@ComponentModelConfig` | 类 | 组件模型 | `componentModel`(非SpringBoot设"default") |
| `@Immutable` | 类 | 不可变标记 | 标记后update方法不修改target |

> 注解的完整属性定义请查阅 [references/annotations.md](references/annotations.md)

## 常见场景示例

### 自定义属性映射

```java
@AutoMapper(target = CarDto.class)
@Data
public class Car {
    @AutoMapping(target = "seat")              // 不同属性名
    private SeatConfiguration seatConfiguration;

    @AutoMapping(dateFormat = "yyyy-MM-dd HH:mm:ss")  // 日期格式
    private LocalDateTime orderTime;

    @AutoMapping(numberFormat = "$0.00")        // 数字格式
    private BigDecimal price;

    @AutoMapping(ignore = true)                // 忽略映射
    private String internal;

    @AutoMapping(defaultValue = "0")           // null时默认值
    private Integer count;

    @AutoMapping(expression = "java(String.join(\",\", source.getList()))")  // 表达式
    private List<String> list;
}
```

### 一对多转换

```java
@AutoMappers({
    @AutoMapper(target = UserDto.class),
    @AutoMapper(target = UserVO.class)
})
@Data
public class User {
    @AutoMapping(targetClass = UserDto.class, target = "educations")
    private List<String> educationList;

    @AutoMappings({
        @AutoMapping(targetClass = UserDto.class, dateFormat = "yyyy-MM-dd"),
        @AutoMapping(targetClass = UserVO.class, ignore = true)
    })
    private Date birthday;
}
```

### 枚举自动转换

```java
@AutoEnumMapper("code")
@Getter @AllArgsConstructor
public enum StatusEnum {
    ENABLED(1), DISABLED(0);
    private final Integer code;
}

// 实体中直接用枚举类型，会自动与 code 值互相转换
@AutoMapper(target = GoodsVo.class)
@Data
public class Goods {
    private StatusEnum status;  // 自动转为 Integer
}
```

### Map 转对象

```java
@AutoMapMapper
@Data
public class ConfigModel {
    private String name;
    private Integer value;
}
// converter.convert(map, ConfigModel.class)
```
需要引入 `hutool-core` 依赖（1.4.0+版本）。

### 循环引用（树形结构等）

```java
@AutoMapper(target = TreeNodeDto.class, cycleAvoiding = true)
@Data
public class TreeNode {
    private TreeNode parent;
    private List<TreeNode> children;
}
```

### 自定义转换器

```java
@Component
@Named("TitleTranslator")
public class Titles {
    @Named("EnglishToFrench")
    public String toFrench(String title) { ... }
}

@AutoMapper(target = FrenchRelease.class, uses = Titles.class)
@Data
public class EnglishRelease {
    @AutoMapping(qualifiedByName = "EnglishToFrench")
    private String title;
}
```

### 反向映射配置

```java
@AutoMapper(target = Target.class)
@Data
public class Source {
    @AutoMapping(target = "targetName")
    @ReverseAutoMapping(source = "targetName", target = "sourceName")
    private String sourceName;
}
```

### 嵌套对象自动转换

当属性是自定义类型时，只要该类型也配置了 `@AutoMapper`，会自动递归转换：

```java
@AutoMapper(target = CarDto.class)
public class Car {
    private Seat seat;  // 自动使用 Seat 的转换器
}

@AutoMapper(target = SeatDto.class)
public class Seat { ... }
```

## Converter API

`io.github.linpeilie.Converter` 统一转换入口:

| 方法 | 说明 |
|------|------|
| `convert(source, TargetClass.class)` | 创建新目标对象 |
| `convert(source, existingTarget)` | 更新已有对象 |
| `convert(list, TargetClass.class)` | 列表批量转换 |
| `convert(source, Class, Consumer)` | 转换 + 后处理 |
| `convert(source, Class, CycleAvoidingMappingContext)` | 循环引用安全转换 |
| `convert(Map, Class)` | Map 转对象（需 @AutoMapMapper） |

## 常见问题

| 问题 | 解决方案 |
|------|------|
| "cannot find converter" | 检查依赖/注解处理器, `mvn clean compile`, 检查 Spring 扫描包 |
| "Couldn't retrieve @Mapper" | MapStruct 依赖冲突, 排除其他依赖中的 MapStruct |
| NoSuchMethodError: Adapter | 多模块冲突, 配置不同 `adapterPackage` |
| spring-boot-devtools 异常 | 去掉该依赖, 会修改 ClassLoader |
| 生成代码位置 | `target/generated-sources` 目录 |

> 完整排查指南请查阅 [references/troubleshooting.md](references/troubleshooting.md)
> 注解完整属性请查阅 [references/annotations.md](references/annotations.md)
> 配置项详情请查阅 [references/configuration.md](references/configuration.md)

