如何在Spring Boot项目中使用MapStruct?
在 Spring Boot 项目中使用 MapStruct 非常简单,只需几个步骤即可完成配置并发挥其强大的对象映射能力。下面是详细的集成方法和示例:
1. 添加依赖
在 pom.xml
中添加 MapStruct 依赖,Spring Boot 会自动处理相关配置:
<properties><mapstruct.version>1.5.5.Final</mapstruct.version>
</properties><dependencies><!-- MapStruct 核心依赖 --><dependency><groupId>org.mapstruct</groupId><artifactId>mapstruct</artifactId><version>${mapstruct.version}</version></dependency>
</dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><source>17</source> <!-- 根据你的JDK版本调整 --><target>17</target><annotationProcessorPaths><!-- MapStruct 注解处理器 --><path><groupId>org.mapstruct</groupId><artifactId>mapstruct-processor</artifactId><version>${mapstruct.version}</version></path><!-- Lombok 处理器(如果使用Lombok) --><path><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.24</version></path><path><groupId>org.projectlombok</groupId><artifactId>lombok-mapstruct-binding</artifactId><version>0.2.0</version></path></annotationProcessorPaths></configuration></plugin></plugins>
</build>
2. 创建映射器接口
在 Spring Boot 中,推荐使用 componentModel = "spring"
让 MapStruct 生成的映射器被 Spring 容器管理,方便依赖注入:
示例代码:
package com.example.demo.controller;import com.example.demo.dto.UserDTO;
import com.example.demo.entity.User;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("/users")
public class UserController {@Autowiredprivate UserService userService;@GetMapping("/{id}")public UserDTO getUser(@PathVariable Long id) {return userService.getUserInfo(id);}@PostMappingpublic User createUser(@RequestBody UserDTO userDTO) {return userService.saveUser(userDTO);}
}package com.example.demo.dto;import lombok.Data;
import java.time.LocalDate;@Data
public class UserDTO {private Long id;private String userName; // 与实体类的username不同private Integer userAge; // 与实体类的age不同private LocalDate birthDate; // 与实体类的birthday不同private String emailAddress; // 与实体类的email不同
}package com.example.demo.entity;import lombok.Data;
import java.time.LocalDate;@Data
public class User {private Long id;private String username;private String password;private Integer age;private LocalDate birthday;private String email;
}package com.example.demo.mapper;import com.example.demo.dto.UserDTO;
import com.example.demo.entity.User;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Named;
import org.mapstruct.factory.Mappers;// componentModel = "spring" 表示让Spring管理该映射器
@Mapper(componentModel = "spring")
public interface UserMapper {// 如果不使用Spring注入,可以通过此方式获取实例UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);// 基本映射,处理字段名不同的情况@Mapping(source = "username", target = "userName")@Mapping(source = "age", target = "userAge")@Mapping(source = "birthday", target = "birthDate")@Mapping(source = "email", target = "emailAddress", qualifiedByName = "maskEmail")UserDTO toDTO(User user);// 反向映射@Mapping(source = "userName", target = "username")@Mapping(source = "userAge", target = "age")@Mapping(source = "birthDate", target = "birthday")@Mapping(source = "emailAddress", target = "email")User toEntity(UserDTO dto);// 自定义映射方法:对邮箱进行简单脱敏@Named("maskEmail")default String maskEmail(String email) {if (email == null || !email.contains("@")) {return email;}String[] parts = email.split("@");if (parts[0].length() > 3) {return parts[0].substring(0, 3) + "***@" + parts[1];}return parts[0] + "***@" + parts[1];}
}package com.example.demo.service;import com.example.demo.dto.UserDTO;
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.time.LocalDate;@Service
public class UserService {// 注入MapStruct生成的映射器@Autowiredprivate UserMapper userMapper;public UserDTO getUserInfo(Long id) {// 模拟从数据库获取用户实体User user = new User();user.setId(id);user.setUsername("zhang_san");user.setPassword("encrypted_password");user.setAge(28);user.setBirthday(LocalDate.of(1995, 3, 15));user.setEmail("zhangsan@example.com");// 使用映射器转换为DTOreturn userMapper.toDTO(user);}public User saveUser(UserDTO dto) {// 将DTO转换为实体User user = userMapper.toEntity(dto);// 模拟保存到数据库的逻辑user.setPassword("encrypted_" + dto.getUserName());return user;}
}
关键配置说明
-
@Mapper(componentModel = "spring")
:- 这个配置让 MapStruct 生成的映射器实现类会被标记为
@Component
,从而可以被 Spring 自动扫描并纳入容器管理 - 之后就可以通过
@Autowired
轻松注入映射器,无需手动创建实例
- 这个配置让 MapStruct 生成的映射器实现类会被标记为
-
与 Lombok 兼容:
- 如果项目中使用 Lombok,需要添加
lombok-mapstruct-binding
依赖解决注解处理器冲突 - 确保 Lombok 版本与 MapStruct 版本兼容(示例中使用的组合经过验证)
- 如果项目中使用 Lombok,需要添加
-
生成的代码位置:
- 编译后,MapStruct 会在
target/generated-sources/annotations
目录下生成映射器的实现类 - 可以查看这些代码了解映射逻辑,便于调试
- 编译后,MapStruct 会在
运行效果
当访问 GET /users/1
时,会返回如下 JSON:
{"id": 1,"userName": "zhang_san","userAge": 28,"birthDate": "1995-03-15","emailAddress": "zha***@example.com"
}
(注意邮箱已通过自定义映射方法进行脱敏处理)
项目中的最佳实践
- 按业务模块组织映射器:每个领域模型创建独立的映射器接口
- 处理复杂映射:对于跨对象的复杂映射,使用
@Mapping
的expression
属性或自定义方法 - 集合映射:直接声明集合转换方法(如
List<UserDTO> toDTOList(List<User> users)
),MapStruct 会自动生成循环转换逻辑 - 测试映射器:编写单元测试验证映射结果,确保字段映射正确
- 版本兼容:保持 MapStruct 与 JDK、Spring Boot 版本的兼容性
通过这种方式,MapStruct 能够完美融入 Spring Boot 生态,大幅减少对象转换的样板代码,同时保持类型安全和高性能。