Java中MapStruct对象映射的实现
简介
MapStruct是一种实体类映射框架,能够通过Java注解将一个实体类的属性安全地赋值给另一个实体类。有了mapstruct,只需要定义一个映射器接口,声明需要映射的方法,在编译过程中,mapstruct会自动生成该接口的实现类,实现将源对象映射到目标对象的效果。
MapStruct是基于JSR 269实现的,JSR 269是JDK引进的一种规范。有了它,能够实现在编译期处理注解,并且读取、修改和添加抽象语法树中的内容。JSR 269使用Annotation Processor在编译期间处理注解,Annotation Processor相当于编译器的一种插件,因此又称为插入式注解处理。官网通道 | Github
优点
安全性高:由于映射是在编译期间实现的,如果编译器能够通过,运行期就不会报错。
高性能:编译时生成bean映射的实现类,通过使⽤普通⽅法(getter/setter)调⽤⽽不是反射来快速执⾏。
缺点
使用复杂度:对于更复杂的映射,需要开发人员编写自定义映射接口和函数。
同类对比
映射工具 | 实现机制 | 性能对比 | 备注 |
Dozer | 反射机制 | 中 | 使用递归将数据从一个对象复制到另一个对象 |
Orika | 反射机制 | 中 | 同Dozer,不过Orika 使用字节码生成 |
ModelMapper | 反射机制 | 中 | 简单易用,它根据约定确定对象之间的映射方式 |
JMapper | 编译生成 | 高 | 基于Javassist 的Java映射框架 |
MapStruct | 编译生成 | 高 | 在编译时生成bean映射,以确保高性能、彻底的错误检查 |
快速入门
Maven依赖
<!-- 定义版本 --> <properties> <org.mapstruct.version>1.6.0</org.mapstruct.version> <org.projectlombok.mapstruct.version>0.2.0</org.projectlombok.mapstruct.version> </properties> <!-- 依赖包 --> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct</artifactId> <version>${org.mapstruct.version}</version> </dependency> <!--MapStruct会用到对象中的get、set方法,但get、set方法又需要lombok来生成。因此需要控制这两者工作顺序--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok-mapstruct-binding</artifactId> <version>${org.projectlombok.mapstruct.version}</version> </dependency>
编译插件
<!-- 方式一--> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>${org.mapstruct.version}</version> <scope>provided</scope> </dependency> <!-- 方式二--> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <source>17</source> <target>17</target> <annotationProcessorPaths> <path> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>${org.mapstruct.version}</version> </path> </annotationProcessorPaths> </configuration> </plugin> </plugins> </build>
Model定义
@Data @Builder @AllArgsConstructor @NoArgsConstructor public class UserDTO implements Serializable { private Integer id; private String userName; private String password; private Integer age; private String address; private String email; private List<UserRole> roles; } @Data @Builder @AllArgsConstructor @NoArgsConstructor public class UserVO implements Serializable { private Integer id; private String name; private String pwd; private Integer age; private String email; private List<UserRole> roles; } @Data @Builder @AllArgsConstructor @NoArgsConstructor public class UserRole implements Serializable { private Integer roleId; private String roleName; private String remark; }
Mapper定义
//@Mapper(componentModel = "spring") @Mapper public interface MapStructMapper { MapStructMapper INSTANCE = Mappers.getMapper(MapStructMapper.class); /** * 单个对象转换 * @param userDTO * @return */ UserVO userDtoToVO(UserDTO userDTO); /** * 集合对象转换 * @param userDTO * @return */ List<UserVO> userDtoToVOList(List<UserDTO> userDTO); }
重要:SpringBoot项目可以使用@Mapper(componentModel = "spring")的方式将bean交给spring容器进行管理,因此可以不用写MapStructMapper INSTANCE = Mappers.getMapper(MapStructMapper.class);
转换调用
public void mapStructConvertTest(){ // 初始化用户 UserDTO userDTO = this.instanceUser(); UserVO userVO = MapStructMapper.INSTANCE.userDtoToVO(userDTO); log.info("userVO:{}", JSON.toJSONString(userVO)); }
场景示例
常规映射
@Mapper public interface MapStructMapper { MapStructMapper INSTANCE = Mappers.getMapper(MapStructMapper.class); UserVO userDtoToVO(UserDTO userDTO); }
集合映射
@Mapper public interface MapStructMapper { MapStructMapper INSTANCE = Mappers.getMapper(MapStructMapper.class); List<UserVO> userDtoToVOList(List<UserDTO> userDTO); }
重要:集合转换时,必须先有单个对象的转换函数。并且Mapper中只能有一个同类的原对象与目标对象的转换,否则集合转换不知道取哪一个单对象转换函数。
单字段映射
@Mapping(target = "pwd", source = "password") UserVO userDtoToVO(UserDTO userDTO);
多字段映射
@Mappings({ @Mapping(target = "pwd", source = "password"), @Mapping(target = "name", source = "userName"), }) UserVO userDtoToVOMoreField(UserDTO userDTO);
忽略字段
@Mapping(target = "email", ignore = true) UserVO userDtoToVOIgnoreField(UserDTO userDTO);
常量值映射
@Mapping(target = "constant", constant = "OK") UserVO newUserWithConstant(UserDTO userDTO);
默认值映射
@Mapping(source = "email", target = "email", defaultValue = "默认值") UserVO userDtoToVONullDefaultValue(UserDTO userDTO);
表达式映射
@Mapping(target = "fullName", expression = "java(userDTO.getUserName() + ' ' + userDTO.getAddress())") UserVO userDtoToVOExpression(UserDTO userDTO);
@Mapping(target = "email", expression = "java(!userDTO.getEmail().isEmpty()? \"不为空\" : \"为空\")") UserVO userDtoToVOWithCondition(UserDTO userDTO);
执行函数
@Mapping(target = "email", source = "email", qualifiedByName = "toUpperCase") UserVO emailToUpperCase(UserDTO userDTO); @Named("toUpperCase") default String toUpperCase(String value) { // 转换大写 return value == null ? null : value.toUpperCase(); }
深拷贝
@Mapper(componentModel = "spring",mappingControl = DeepClone.class) public interface MapStructMapper { }
说明:mappingControl = DeepClone.class 是指定深拷贝模式,不指定则默认浅拷贝,浅拷贝时集合类是底层是调用Array 的copy 方法。如果是深拷贝模式,MapStruct框架会生成集合遍历代码,集合中元素如果是引用类型会生成引用类型转换代码,层层转换,深度拷贝。集合类拷贝的限制比较多,不支持多层嵌套集合类深拷贝,而且要求源字段和目标字段集合类型严格一致。
浅拷贝 :只复制对象的引用,而不会复制对象本身的内容。如果更改了原始对象的一个地址,DTO中的地址也会跟着改变,因为它们指向的是同一个对象。
深拷贝:会递归地复制对象的所有内容,包括嵌套的对象。即使你更改了原始对象中的数据,DTO中的数据也不会受到影响。
逆向映射
/** * 单个对象映射 * @param userDTO * @return */ UserVO userDtoToVO(UserDTO userDTO); /** * 逆向映射 * @param userVO 源 VO 对象 * @return 目标 DTO 对象 */ @InheritInverseConfiguration UserDTO userVOToDto(UserVO userVO);
说明:正向函数有业务逻辑处理或属性类型不匹配的不能逆向映射
映射后执行动作
@AfterMapping default void afterMapping(UserDTO userDTO, @MappingTarget UserVO userVO) { // Add custom post-mapping logic here userVO.setId(10000); System.out.println("afterMapping:id设置为:"+userVO.getId()); }
延伸内容
MapStruct Plus 是 MapStruct 的增强工具,在 MapStruct 的基础上,实现了自动生成 Mapper 接口的功能,并强化了部分功能,使 Java 类型转换更加便捷、优雅。官网通道 | GitHub
Maven依赖
<properties> <mapstruct-plus.version>1.4.5</mapstruct-plus.version> </properties> <dependency> <groupId>io.github.linpeilie</groupId> <artifactId>mapstruct-plus-spring-boot-starter</artifactId> <version>${mapstruct-plus.version}</version> </dependency>
新增配置类
@ComponentModelConfig(componentModel = "default") public class MapperConfiguration { }
对象映射
@AutoMapper(target = UserDto.class) @Data public class User { // ... }
转换Map
@AutoMapMapper @Data public class MapModelB { private Date date; }
一个类转换为多个类
@Data @AutoMappers({ @AutoMapper(target = UserDto.class), @AutoMapper(target = UserVO.class) }) public class User { // fields }
循环嵌套
@Data @AutoMapper(target = TreeNodeDto.class, cycleAvoiding = true) public class TreeNode { private TreeNode parent; private List<TreeNode> children; } @Data @AutoMapper(target = TreeNode.class, cycleAvoiding = true) public class TreeNodeDto { private TreeNodeDto parent; private List<TreeNodeDto> children; }
到此这篇关于Java中MapStruct对象映射的实现的文章就介绍到这了,更多相关MapStruct对象映射内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
SpringBoot+MybatisPlus+Mysql+Sharding-JDBC分库分表
本文主要介绍了SpringBoot+MybatisPlus+Mysql+Sharding-JDBC分库分表,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下2022-03-03往DAO类中注入@PersistenceContext和@Resource的区别详解
这篇文章主要介绍了往DAO类中注入@PersistenceContext和@Resource的区别详解,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教2022-02-02
最新评论