一、Mapstruct简介
MapStruct是用于生成类型安全的bean映射类的Java注解处理器。
你所要做的就是定义一个映射器接口,声明任何需要映射的方法。在编译过程中,MapStruct将生成该接口的实现。此实现使用纯Java的方法调用源对象和目标对象之间进行映射,并非Java反射机制。
与手工编写映射代码相比,MapStruct通过生成冗长且容易出错的代码来节省时间。在配置方法的约定之后,MapStruct使用了合理的默认值,但在配置或实现特殊行为时将不再适用。
与动态映射框架相比,MapStruct具有以下优点:
- 使用纯Java方法代替Java反射机制快速执行
- 编译时类型安全:只能映射彼此的对象和属性,不能映射一个Order实体到一个Customer DTO中等等
- 如果无法映射实体或属性,则在编译时清楚错误报告
二、Mapstruct原理
MapStruct是基于JSR 269的Java注解处理器,因此可以在命令行构建中使用(javac、Ant、Maven等等),也可以在IDE内使用。
它包括以下工件:
- org.mapstruct:mapstruct:包含了必要的注解,例如@Mapping;在Java 8或更高版本中,使用org.mapstruct:mapstruct-jdk8,而不是利用Java 8中引入的语言进行改进。
- org.mapstruct:mapstruct-processor:包含生成映射器实现的注解处理器
这就是为什么mapstruct的效率比较高的原因,相比于反射获取对象进行拷贝的方法,这种更贴近于原生get、set方法的框架显得更为高效。
这个文件是通过在mapper中的注解,使用生成映射器的注解处理器从而自动生成了这段代码。
看到这里是不是感觉JSR 269注解处理器很熟悉。确实在很多地方都是用到了他,在我之前了解lombok原理时也看到他的身影。那么总让我在这里好好介绍一下他。
三、注解处理器
1.Java代码编译过程
Java代码编译和执行的整个过程包含了以下三个重要的机制:1)Java源码编译机制;2)类加载机制;3)类执行机制
其中,Java源码编译由以下三个过程组成:1)分析和输入到符号表;2)注解处理;3)语义分析和生成class文件
流程图如下所示:其中的annotation processing就是代码的注解处理,jdk7之前访问和处理Annotation的工具统称APT(Annotation Processing Tool)(jdk7后就被废除了),jdk7及之后采用了JSR 269 API。
2.注解处理器的作用
Annotation就像代码里的特殊标记,这些标记可以在编译、类加载、运行时被读取。读取到了程序元素的元数据,就可以执行相应的处理。通过注解,程序开发人员可以在不改变原有逻辑的情况下,在源代码文件中嵌入一些补充信息。代码分析工具、开发工具和部署工具可以通过解析这些注解获取到这些补充信息,从而进行验证或者进行部署等。
————————————————
版权声明:本文为CSDN博主「古柏树下」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/sinat_29774479/article/details/90402767
四 使用
mapstruct 专门用来处理 domin 实体类与 model 类的属性映射的,我们只需定义每个实体的mapper转换接口,mapstruct 在编译的时候就会自动的帮我们实现这个映射接口,避免了麻烦复杂的映射实现
简单说:快捷实现domain 实体与DTO 、VO实体的映射转化。
使用
引入
在pom.xml中引入jar包
org.mapstruct
mapstruct
1.3.1.Final
org.mapstruct
mapstruct-processor
1.3.1.Final
注:mapstruct-jdk8 这种引用方式官方已经废弃
org.mapstruct
mapstruct-jdk8
1.3.1.Final
使用方式
spring 中注入:
@Mapper
public interface UserRoleMapper {
/**
* 获取该类自动生成的实现类的实例
* 接口中的属性都是 public static final 的 方法都是public abstract的
*/
UserRoleMapper INSTANCES = Mappers.getMapper(UserRoleMapper.class);
/**
* 这个方法就是用于实现对象属性复制的方法
*
* @Mapping 用来定义属性复制规则 source 指定源对象属性 target指定目标对象属性
*
* @param user 这个参数就是源对象,也就是需要被复制的对象
* @return 返回的是目标对象,就是最终的结果对象
*/
@Mappings({
@Mapping(source = "id", target = "userId"),
@Mapping(source = "username", target = "name"),
@Mapping(source = "role.roleName", target = "roleName")
})
UserRoleDto toUserRoleDto(User user);
@RestController
public class DemoMapstructApplication {
@Autowired
private UserRoleMapper mapper;
@GetMapping("convert3")
public String convertEntity3() {
Role role = new Role(2L, "administrator", "超级管理员");
user = new User(1L, "zhangsan", "12345", "17677778888", "123@qq.com", role);
UserRoleDto userRoleDto = mapper.toUserRoleDto(user);
return userRoleDto.toString();
}
}
javabeen:
@Data
@ToString
public class StudentDto {
private String name;
private String status;
private Integer age;
}
@Data
@AllArgsConstructor
public class StudentEntity {
private String name;
private String status;
private Integer age;
private String girlFriend;
}
/**
* @author zyh
*/
@Mapper
public interface StudentObjMapping {
StudentObjMapping INSTANCES=Mappers.getMapper(StudentObjMapping.class);
StudentDto toStudentDto(StudentEntity entity);
}
注意哟,女朋友没带过来,因为字段没对应的问题
public class ObjTraTest {
public static void main(String[] args) {
StudentEntity studentDto=new StudentEntity("zyh","1",24,"zmy");
StudentDto dto = StudentObjMapping.INSTANCES.toStudentDto(studentDto);
System.out.println(dto.toString());
}
}
如果我们两个实体有不同的名字咋办嘞?
参考下列demo,
/** @Mapping 用来定义属性复制规则 source 指定源对象属性 target指定目标对象属性
*
* @param user 这个参数就是源对象,也就是需要被复制的对象
* @return 返回的是目标对象,就是最终的结果对象
*/
@Mappings({
@Mapping(source = "id", target = "userId"),
@Mapping(source = "username", target = "name"),
@Mapping(source = "role.roleName", target = "roleName")
})
UserRoleDto toUserRoleDto(User user);
另外这里支持多数据源,只要我们把源和目标对应就好。
多家俩类,并更新下mapping
@Data
@ToString
public class People {
private String peopleName;
private int age;
private String status;
private String home;
}
@Data
@AllArgsConstructor
public class StudentHome {
private String homeAddr;
}
@Mapper
public interface StudentObjMapping {
StudentObjMapping INSTANCES=Mappers.getMapper(StudentObjMapping.class);
StudentDto toStudentDto(StudentEntity entity);
@Mappings({
@Mapping(source = "student.name",target = "peopleName"),
@Mapping(source = "home.homeAddr",target = "home")
})
People toPeople(StudentEntity student,StudentHome home);
}
对于List的转换,且其各元素有特定的对应关系,需要先些普通实体的转换,再写list的转换
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
public interface UserMapping {
/**
* Student 转化为 User
* @param Student
* @return
*/
@Mappings({
@Mapping(target = "uname", source = "sname")
// 多个属性不对应可以用 "," 隔开编写多个@Mapping
// ,@Mapping(target = "uname", source = "sname")
})
User studentToUser(Student student);
/**
* 此时 studentsToUsers 的实现为循环调用 studentToUser 并继承了 studentToUser 的属性映射
* Students 转化为 Users
* @param Students
* @return
*/
List studentsToUsers(List students);
}
问题总结
MapStruct需要Impl类
参考: java - MapStruct需要实现类
没有引入 mapstruct-processor-xx 包导致的
参考
MapStruct原理分析
MapStruct超级简单的学习笔记_qq122516902的博客-CSDN博客_mapstruct
干掉 BeanUtils!试试这款 Bean 自动映射工具,真心强大!