目录
- 一、 什么是 MapStruct?
- 二、 MapStruct 能做什么?
- 三、 MapStruct 在 Spring Boot 中的使用
- 四、 MapStruct 的优缺点
- 五、 总结
🌟我的其他文章也讲解的比较有趣😁,如果喜欢博主的讲解方式,可以多多支持一下,感谢🤗!
🌟了解 SpringBoot 常用注解请看: SpringBoot常用注解,一文搞懂!
其他优质专栏: 【🎇SpringBoot】【🎉多线程】【🎨Redis】【✨设计模式专栏(已完结)】…等
如果喜欢作者的讲解方式,可以点赞收藏加关注,你的支持就是我的动力
✨更多文章请看个人主页: 码熔burning
前言:
在 Spring Boot 项目中,Bean 转换是常见的任务。手动编写转换代码不仅繁琐,而且容易出错。MapStruct 作为一个强大的 Java Bean 映射工具,可以自动生成类型安全的转换代码,极大地简化开发工作。本文将由浅入深地介绍 MapStruct 的概念、功能,并通过多个 Spring Boot 实例演示其用法,最后分析其优缺点,帮助开发者更好地理解和使用 MapStruct。
一、 什么是 MapStruct?
MapStruct 是一个 Java 注解处理器,用于生成类型安全的 Bean 映射代码。它通过分析接口和注解,在编译时生成高效的 Bean 转换实现,避免了手动编写大量重复的转换代码。MapStruct 的目标是简化 Bean 映射过程,提高开发效率,并减少潜在的错误。简单来说,它就是一个帮你自动生成 Bean 转换代码的工具。
- MapStruct 官方文档
- MapStruct GitHub 仓库
二、 MapStruct 能做什么?
MapStruct 主要用于解决以下问题:
- Bean 之间的属性复制: 将一个 Bean 对象的属性值复制到另一个 Bean 对象中。
- 类型转换: 自动进行基本类型、字符串、日期等类型之间的转换。
- 复杂类型转换: 处理嵌套对象、集合、枚举等复杂类型的转换。
- 自定义转换逻辑: 允许开发者自定义转换规则,满足特定的业务需求。
- 减少样板代码: 自动生成转换代码,避免手动编写大量的
getter/setter
调用。 - 提高性能: 生成的代码通常比手动编写的代码更高效,因为它直接访问属性,避免了反射。
三、 MapStruct 在 Spring Boot 中的使用
下面通过多个 Spring Boot 示例来演示 MapStruct 的用法,从最简单的属性复制开始,逐步深入到复杂类型的转换和自定义逻辑。
1. 简单属性复制
-
项目准备
- 创建一个 Spring Boot 项目。
- 添加 MapStruct 依赖:
<dependency><groupId>org.mapstruct</groupId><artifactId>mapstruct</artifactId><version>1.5.5.Final</version> </dependency> <dependency><groupId>org.mapstruct</groupId><artifactId>mapstruct-processor</artifactId><version>1.5.5.Final</version><scope>provided</scope> </dependency>
-
定义源对象和目标对象
// 源对象:User @Data public class User {private Long id;private String username;private String email; }// 目标对象:UserDTO @Data public class UserDTO {private Long id;private String username;private String email; }
-
创建 Mapper 接口
import org.mapstruct.Mapper;@Mapper(componentModel = "spring") // 使用 Spring 的依赖注入 public interface UserMapper {UserDTO userToUserDTO(User user); }
解释:
@Mapper(componentModel = "spring")
:指定使用 Spring 的依赖注入,MapStruct 会自动生成一个 Spring Bean。UserDTO userToUserDTO(User user)
:定义一个转换方法,将User
对象转换为UserDTO
对象。由于属性名相同,MapStruct 会自动进行属性复制。
-
使用 Mapper
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service;@Service public class UserService {@Autowiredprivate UserMapper userMapper;public UserDTO convertUserToUserDTO(User user) {return userMapper.userToUserDTO(user);} }
-
测试
import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest;import static org.junit.jupiter.api.Assertions.assertEquals;@SpringBootTest public class MapStructTest {@Autowiredprivate UserService userService;@Testpublic void testConvertUserToUserDTO() {User user = new User();user.setId(1L);user.setUsername("testUser");user.setEmail("test@example.com");UserDTO userDTO = userService.convertUserToUserDTO(user);assertEquals(user.getId(), userDTO.getId());assertEquals(user.getUsername(), userDTO.getUsername());assertEquals(user.getEmail(), userDTO.getEmail());} }
注意:如果映射失败,加上这个注解:
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok-mapstruct-binding</artifactId><version>0.2.0</version>
</dependency>
2. 属性名不同时的映射
-
修改目标对象
// 目标对象:UserDTO @Data public class UserDTO {private Long id;private String name; // 属性名修改为 nameprivate String emailAddress; // 属性名修改为 emailAddress }
-
修改 Mapper 接口
import org.mapstruct.Mapper; import org.mapstruct.Mapping;@Mapper(componentModel = "spring") public interface UserMapper {@Mapping(source = "username", target = "name")@Mapping(source = "email", target = "emailAddress")UserDTO userToUserDTO(User user); }
解释:
@Mapping(source = "username", target = "name")
:指定源对象的username
属性映射到目标对象的name
属性。@Mapping(source = "email", target = "emailAddress")
:指定源对象的email
属性映射到目标对象的emailAddress
属性。
3. 类型转换和自定义表达式
-
修改源对象和目标对象
// 源对象:User @Data public class User {private Long id;private String username;private String email;private int age;private Date birthday; }// 目标对象:UserDTO @Data public class UserDTO {private Long id;private String name;private String emailAddress;private String age; // 类型修改为 Stringprivate String birthDate; // 类型修改为 String }
-
修改 Mapper 接口
import org.mapstruct.Mapper; import org.mapstruct.Mapping; import java.text.SimpleDateFormat; import java.util.Date;@Mapper(componentModel = "spring") public interface UserMapper {@Mapping(source = "username", target = "name")@Mapping(source = "email", target = "emailAddress")@Mapping(expression = "java(String.valueOf(user.getAge()))", target = "age")@Mapping(source = "birthday", target = "birthDate", dateFormat = "yyyy-MM-dd")UserDTO userToUserDTO(User user); }
解释:
@Mapping(expression = "java(String.valueOf(user.getAge()))", target = "age")
:使用 Java 表达式进行自定义转换,将age
从int
转换为String
。@Mapping(source = "birthday", target = "birthDate", dateFormat = "yyyy-MM-dd")
:使用dateFormat
指定日期格式,将Date
转换为String
。
4. 使用自定义方法进行转换
-
修改 Mapper 接口
import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.Named;@Mapper(componentModel = "spring") public interface UserMapper {@Mapping(source = "username", target = "name")@Mapping(source = "email", target = "emailAddress")@Mapping(source = "age", target = "age", qualifiedByName = "ageToString")UserDTO userToUserDTO(User user);@Named("ageToString")default String ageToString(int age) {return String.valueOf(age);} }
解释:
@Mapping(source = "age", target = "age", qualifiedByName = "ageToString")
:指定使用名为ageToString
的自定义方法进行转换。@Named("ageToString")
:标记自定义方法的名称。default String ageToString(int age)
:定义一个默认方法,将age
从int
转换为String
。
四、 MapStruct 的优缺点
优点
- 类型安全: 在编译时检查映射关系,避免运行时错误。
- 高性能: 生成的代码直接访问属性,避免了反射,性能优于手动编写的代码。
- 易于使用: 通过注解简化了映射配置,减少了样板代码。
- 可定制性: 允许自定义转换逻辑,满足特定的业务需求。
- 可读性强: 映射关系清晰明了,易于理解和维护。
- 编译时生成代码: 映射代码在编译时生成,不会影响运行时性能。
- 支持多种配置方式: 可以使用注解、表达式、自定义方法等多种方式进行配置。
缺点
- 学习成本: 需要学习 MapStruct 的注解和配置方式。
- 编译时依赖: 需要在编译时添加 MapStruct 的依赖。
- 调试困难: 生成的代码不容易调试,需要查看生成的代码才能了解具体的映射逻辑。
- 不支持动态映射: 映射关系在编译时确定,不支持动态修改。
- 复杂映射配置: 对于复杂的映射关系,配置可能会比较繁琐。
- 编译速度: 在大型项目中,使用 MapStruct 可能会增加编译时间。
五、 总结
MapStruct 是一个强大的 Bean 映射工具,可以极大地简化 Java 开发中的 Bean 转换工作。它具有类型安全、高性能、易于使用等优点,但也存在一些缺点。在 Spring Boot 项目中,使用 MapStruct 可以提高开发效率,减少错误,并提高代码的可维护性。开发者可以根据自己的实际情况选择是否使用 MapStruct。通过本文由浅入深的介绍和示例,相信你已经对 MapStruct 有了更深入的了解,可以开始在自己的项目中尝试使用它了。