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 环境(推荐):
<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 中必须添加):
<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:
<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:
@AutoMapper(target = UserDto.class)
@Data
public class User {
private String username;
private int age;
}
3. 执行转换
// 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
常见场景示例
自定义属性映射
@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;
}
一对多转换
@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;
}
枚举自动转换
@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 转对象
@AutoMapMapper
@Data
public class ConfigModel {
private String name;
private Integer value;
}
// converter.convert(map, ConfigModel.class)
需要引入 hutool-core 依赖(1.4.0+版本)。
循环引用(树形结构等)
@AutoMapper(target = TreeNodeDto.class, cycleAvoiding = true)
@Data
public class TreeNode {
private TreeNode parent;
private List<TreeNode> children;
}
自定义转换器
@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;
}
反向映射配置
@AutoMapper(target = Target.class)
@Data
public class Source {
@AutoMapping(target = "targetName")
@ReverseAutoMapping(source = "targetName", target = "sourceName")
private String sourceName;
}
嵌套对象自动转换
当属性是自定义类型时,只要该类型也配置了 @AutoMapper,会自动递归转换:
@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/annotations.md 配置项详情请查阅 references/configuration.md