前言
🤟 找工作,来万码优才: #小程序://万码优才/r6rqmzDaXpYkJZF
基本的Java知识推荐阅读:
事因是在实战中看到这种用法,以往我的用法是这样转换,也可参考学习:Java 8 流式 API 将实体类列表转换为视图对象列表(附Demo)

1. 基本知识
MapStruct 是一个 Java 的代码生成器,专门用于对象映射,可以将一个 Java Bean 自动转换为另一个 Java Bean,而无需手动编写转换逻辑
它的主要特点是基于注解、编译时生成代码、高效无性能损耗,广泛应用于 DTO(数据传输对象)和实体对象之间的转换
| 作用 | 说明 |
|---|---|
| 对象映射 | 主要用于 DTO 和实体类的相互转换,减少重复代码 |
| 提高开发效率 | 省去手写转换逻辑,减少维护成本,提高开发效率 |
| 编译时生成代码 | MapStruct 在编译期生成代码,性能优于运行时反射 |
| 可定制转换逻辑 | 允许手动定义映射规则,比如字段映射、类型转换等 |
| 支持 Spring 依赖注入 | 可以和 Spring 结合,通过 @Mapper(componentModel = “spring”) 让 Spring 自动管理 Mapper |
核心类和方法
-
@Mapper 注解
用于定义接口,该接口会被 MapStruct 自动生成实现类 -
Mappers.getMapper(Class<T> mapperInterface)
用于获取 @Mapper 标注接口的实现类实例 -
默认方法(default)
Java 8 开始支持,允许在接口中提供方法实现
适用于特殊逻辑处理,而不依赖 MapStruct 自动生成 -
其他注解
@Mapping:用于自定义字段映射关系
@Mappings:多个 @Mapping 的集合
@InheritInverseConfiguration:生成反向映射
@Mapper(componentModel = "spring"):与 Spring 集成
上述接口的意义:
MailAccountConvert INSTANCE = Mappers.getMapper(MailAccountConvert.class);
- INSTANCE 是 MapStruct 自动生成实现类的单例,便于全局调用
- 这样做可以避免多次创建实例,提升性能
- 利用 INSTANCE,用户可以直接调用接口中的方法
事先可以引入相关依赖:
<dependencies>
<!-- Spring Boot 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MapStruct 依赖 -->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.5.5.Final</version>
</dependency>
<!-- MapStruct 处理器 (需要让 MapStruct 自动生成转换代码) -->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.5.5.Final</version>
<scope>provided</scope> <!-- 只在编译时使用,不打包 -->
</dependency>
<!-- Lombok (减少 Getter/Setter 代码) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.26</version>
<scope>provided</scope>
</dependency>
<!-- Lombok MapStruct 兼容插件 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>0.2.0</version>
<scope>provided</scope>
</dependency>
<!-- Spring Boot 测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2. Demo
简易Demo 实现简单对象转换:
import lombok.AllArgsConstructor;
import lombok.Data;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Data
@AllArgsConstructor
class Student {
private String username;
private Integer age;
}
@Data
@AllArgsConstructor
class StudentDTO {
private String username;
private Integer age;
}
@Mapper
interface StudentMapper {
StudentMapper INSTANCE = Mappers.getMapper(StudentMapper.class);
StudentDTO toStudentDTO(Student student);
}
public class SimpleDemo {
public static void main(String[] args) {
Student student = new Student("码农", 20);
StudentDTO studentDTO = StudentMapper.INSTANCE.toStudentDTO(student);
System.out.println(studentDTO);
}
}
截图如下:

下面的类和方法都不大一样,总的来说,就是替换去尝试摸索!
类似不一样参数的话:

2.1 基础用法
成员变量名相同时的映射
import lombok.AllArgsConstructor;
import lombok.Data;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Data
@AllArgsConstructor
class User {
private String username;
private Integer age;
}
@Data
@AllArgsConstructor
class UserDTO {
private String username;
private Integer age;
}
@Mapper
interface UserMapper {
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
UserDTO toUserDTO(User user);
}
public class Main {
public static void main(String[] args) {
User user = new User("张三", 25);
UserDTO userDTO = UserMapper.INSTANCE.toUserDTO(user);
System.out.println(userDTO);
}
}
输出如下:
UserDTO(username=张三, age=25)
成员变量名不同时的映射
如果字段名称不同,需要使用 @Mapping 注解指定 source 和 target
import org.mapstruct.Mapping;
@Data
@AllArgsConstructor
class Employee {
private String fullName;
private Integer yearsOld;
}
@Data
@AllArgsConstructor
class EmployeeDTO {
private String name;
private Integer age;
}
@Mapper
interface EmployeeMapper {
EmployeeMapper INSTANCE = Mappers.getMapper(EmployeeMapper.class);
@Mapping(source = "fullName", target = "name")
@Mapping(source = "yearsOld", target = "age")
EmployeeDTO toEmployeeDTO(Employee employee);
}
2.2 进阶用法
多参数源映射
可以将多个对象的字段合并到一个对象中
@Data
@AllArgsConstructor
class Address {
private String city;
private String street;
}
@Data
@AllArgsConstructor
class Person {
private String name;
private Integer age;
private String city;
private String street;
}
@Mapper
interface PersonMapper {
PersonMapper INSTANCE = Mappers.getMapper(PersonMapper.class);
@Mapping(source = "address.city", target = "city")
@Mapping(source = "address.street", target = "street")
Person toPerson(String name, Integer age, Address address);
}
更新现有 Bean
使用 @MappingTarget 更新现有对象,而不是创建新的对象
@Mapper
interface UpdateMapper {
UpdateMapper INSTANCE = Mappers.getMapper(UpdateMapper.class);
@Mapping(target = "username", ignore = true) // 忽略 username 字段
void updateUser(@MappingTarget User user, UserDTO userDTO);
}
映射集合
MapStruct 允许直接映射 List、Set 等集合
@Mapper
interface ListMapper {
ListMapper INSTANCE = Mappers.getMapper(ListMapper.class);
List<UserDTO> toUserDTOList(List<User> users);
}
映射枚举
如果需要将枚举类进行转换,可以手动指定映射关系
enum Role {
ADMIN, USER
}
enum RoleDTO {
ROLE_ADMIN, ROLE_USER
}
@Mapper
interface RoleMapper {
RoleMapper INSTANCE = Mappers.getMapper(RoleMapper.class);
@Mapping(source = "ADMIN", target = "ROLE_ADMIN")
@Mapping(source = "USER", target = "ROLE_USER")
RoleDTO toRoleDTO(Role role);
}
2.3 高级特性
依赖注入
可以让 MapStruct 自动被 Spring 识别并注入
@Mapper(componentModel = "spring")
interface InjectedMapper {
UserDTO userToDTO(User user);
}
映射工厂
可以自定义工厂方法创建对象,而不是直接 new 一个新实例
@Mapper
interface CustomFactoryMapper {
CustomFactoryMapper INSTANCE = Mappers.getMapper(CustomFactoryMapper.class);
default UserDTO userToDTO(User user) {
return new UserDTO(user.getUsername(), user.getAge());
}
}
3. 总结
MapStruct 是 Java 中非常强大的对象转换工具,适用于 DTO 与实体类的转换,避免手写 getter/setter 代码,提高开发效率
掌握 MapStruct 的基本用法、注解规则,可以在实际开发中大幅简化对象转换逻辑
看完上述内容,可能很多内容都了解了!
那么给个实战中比较复杂的代码(源自ruoyi-vue-pro的代码)

后续执行调用过程:

以及

以下是大杂烩的Demo
import lombok.AllArgsConstructor;
import lombok.Data;
import org.mapstruct.*;
import org.mapstruct.factory.Mappers;
import java.util.List;
import java.util.stream.Collectors;
/** * MapStruct 知识点总结: * 1. 成员变量名相同时的映射(直接映射) * 2. 成员变量名不同时的映射(使用 @Mapping 指定 source 和 target) * 3. 多参数源映射(多个对象组合到一个目标对象) * 4. 多层嵌套映射(嵌套对象字段的映射) * 5. 更新现有 Bean(@MappingTarget 更新现有实例) * 6. 映射器工厂(使用默认方法或手动创建对象) * 7. 依赖注入(使用 @Mapper(componentModel = "spring") 进行 Spring 依赖注入) * 8. 数据类型转换(使用 expression 或默认转换规则) * 9. 映射集合(列表、集合等转换) * 10. 映射枚举(枚举值转换) * * 实战使用位置: * - DTO 与实体类转换,降低耦合度,提高代码可读性。 * - 复杂数据结构转换,减少手写转换逻辑的冗余代码。 * - 依赖 Spring 进行自动管理,提高开发效率。 * - 适用于微服务、Web 开发等场景。 */
@Data
@AllArgsConstructor
class User {
private String username;
private Integer age;
}
@Data
@AllArgsConstructor
class UserDTO {
private String username;
private Integer age;
}
@Mapper
interface UserMapper {
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
UserDTO toUserDTO(User user);
}
@Data
@AllArgsConstructor
class Employee {
private String fullName;
private Integer yearsOld;
}
@Data
@AllArgsConstructor
class EmployeeDTO {
private String name;
private Integer age;
}
@Mapper
interface EmployeeMapper {
EmployeeMapper INSTANCE = Mappers.getMapper(EmployeeMapper.class);
@Mapping(source = "fullName", target = "name")
@Mapping(source = "yearsOld", target = "age")
EmployeeDTO toEmployeeDTO(Employee employee);
}
@Data
@AllArgsConstructor
class Address {
private String city;
private String street;
}
@Data
@AllArgsConstructor
class Person {
private String name;
private Integer age;
private String city;
private String street;
}
@Mapper
interface PersonMapper {
PersonMapper INSTANCE = Mappers.getMapper(PersonMapper.class);
@Mapping(source = "address.city", target = "city")
@Mapping(source = "address.street", target = "street")
Person toPerson(String name, Integer age, Address address);
}
@Data
@AllArgsConstructor
class Department {
private String departmentName;
}
@Data
@AllArgsConstructor
class Worker {
private String workerName;
private Integer workerAge;
private Department department;
}
@Data
@AllArgsConstructor
class WorkerDTO {
private String workerName;
private Integer workerAge;
private String departmentName;
}
@Mapper
interface WorkerMapper {
WorkerMapper INSTANCE = Mappers.getMapper(WorkerMapper.class);
@Mapping(source = "department.departmentName", target = "departmentName")
WorkerDTO toWorkerDTO(Worker worker);
}
@Mapper
interface UpdateMapper {
UpdateMapper INSTANCE = Mappers.getMapper(UpdateMapper.class);
@Mapping(target = "username", ignore = true)
void updateUser(@MappingTarget User user, UserDTO userDTO);
}
@Mapper
interface CustomFactoryMapper {
CustomFactoryMapper INSTANCE = Mappers.getMapper(CustomFactoryMapper.class);
default UserDTO userToDTO(User user) {
return new UserDTO(user.getUsername(), user.getAge());
}
}
@Mapper(componentModel = "spring")
interface InjectedMapper {
UserDTO userToDTO(User user);
}
@Data
@AllArgsConstructor
class Order {
private String orderId;
private Long orderAmount;
}
@Data
@AllArgsConstructor
class OrderDTO {
private String orderId;
private String orderAmount;
}
@Mapper
interface OrderMapper {
OrderMapper INSTANCE = Mappers.getMapper(OrderMapper.class);
@Mapping(target = "orderAmount", expression = "java(String.valueOf(order.getOrderAmount()))")
OrderDTO toOrderDTO(Order order);
}
@Mapper
interface ListMapper {
ListMapper INSTANCE = Mappers.getMapper(ListMapper.class);
List<UserDTO> toUserDTOList(List<User> users);
}
enum Role {
ADMIN, USER
}
enum RoleDTO {
ROLE_ADMIN, ROLE_USER
}
@Mapper
interface RoleMapper {
RoleMapper INSTANCE = Mappers.getMapper(RoleMapper.class);
@Mapping(source = "ADMIN", target = "ROLE_ADMIN")
@Mapping(source = "USER", target = "ROLE_USER")
RoleDTO toRoleDTO(Role role);
}
public class MapStructDemo {
public static void main(String[] args) {
User user = new User("张三", 25);
UserDTO userDTO = UserMapper.INSTANCE.toUserDTO(user);
System.out.println("1. " + userDTO);
Employee employee = new Employee("李四", 30);
EmployeeDTO employeeDTO = EmployeeMapper.INSTANCE.toEmployeeDTO(employee);
System.out.println("2. " + employeeDTO);
Address address = new Address("北京", "长安街");
Person person = PersonMapper.INSTANCE.toPerson("王五", 40, address);
System.out.println("3. " + person);
Department department = new Department("IT部");
Worker worker = new Worker("赵六", 35, department);
WorkerDTO workerDTO = WorkerMapper.INSTANCE.toWorkerDTO(worker);
System.out.println("4. " + workerDTO);
UserDTO newUserDTO = new UserDTO("新名字", 28);
UpdateMapper.INSTANCE.updateUser(user, newUserDTO);
System.out.println("5. " + user);
Order order = new Order("O12345", 9999L);
OrderDTO orderDTO = OrderMapper.INSTANCE.toOrderDTO(order);
System.out.println("6. " + orderDTO);
List<User> users = List.of(new User("甲", 22), new User("乙", 23));
List<UserDTO> userDTOList = ListMapper.INSTANCE.toUserDTOList(users);
System.out.println("7. " + userDTOList);
RoleDTO roleDTO = RoleMapper.INSTANCE.toRoleDTO(Role.ADMIN);
System.out.println("8. " + roleDTO);
}
}
4. 拓展
实战使用过程中,需要使用到在线表最后进入历史表中
@Mapper
public interface CabinetSwapConverter {
CabinetSwapConverter INSTANCE = Mappers.getMapper(CabinetSwapConverter.class);
@Mapping(target = "id", ignore = true) // 忽略 id 字段
@Mapping(target = "checkStatus", constant = "2L") // 强制设定 checkStatus 为 2
@Mapping(source = "createTime", target = "createTime") // 复制创建时间
@Mapping(source = "updateTime", target = "updateTime") // 复制更新时间
CabinetSwapDetailDO toDetailDO(CabinetSwapDO cabinetSwapDO);
}

后续调用过程中:



![表情[baoquan]-拾光赋](https://blogs.ink/wp-content/themes/zibll/img/smilies/baoquan.gif)


暂无评论内容