介绍:
MapStruct Plus是MapStruct的增强工具,它基于MapStruct并实现了自动生成Mapper接口的功能,同时强化了部分功能,使得Java类型转换更加便捷、优雅。
一:MapStruct:
MapStruct是一个开源的Java库,它简化了对象到对象映射的过程。这个库是一个代码生成器,基于Java注释处理器,通过定义类转换的接口,自动实现属性转换的具体逻辑。
使用步骤:
- 添加依赖:在项目的构建文件中添加MapStruct的依赖。
- 定义映射器接口:创建一个Java接口,并使用@Mapper注解标记。在接口中定义映射方法,这些方法将自动生成实现。
- 配置映射规则:使用@Mapping注解配置源对象和目标对象之间的属性映射规则。如果属性名称相同,则会自动映射;如果名称不同,则可以通过@Mapping注解指定源属性和目标属性。
- 生成映射代码:在构建项目时,MapStruct注解处理器会自动生成映射器的实现类。
- 使用映射器:在代码中通过映射器的实例调用映射方法,将源对象转换为目标对象。
示例演示:
//定义源对象
class Student{
private Long id;
private String name;
}
//定义目标对象
class StudentBo{
private Long studentId;
private String studentName;
}
//定义映射器接口
@Mapper
public interface StudentMapper {
CarMapper INSTANCE = Mappers.getMapper(StudentMapper.class);
@Mapping(source = "id", target = "studentId")
@Mapping(source = "name", target = "studentName")
StudentBo studentToStudentBo(Student stu);
}
//使用映射器进行对象转换
@Test
public void test{
Student stu= new Student();
stu.setId(1L);
stu.setName("Student转换StudentBo");
StudentBo stuBo= CarMapper.INSTANCE.studentToStudentBo(stu);
System.out.println(stuBo.getStudentId()); // 输出:1
System.out.println(stuBo.getStudentName()); // 输出:Student转换StudentBo
}
缺点:
- 需要定义映射器接口:虽然这有助于明确映射规则,但也增加了额外的代码量。
- 对于非常简单的映射场景,可能显得过于重量级。
下面介绍MapStruct Plus,简化MapStruct。
二:MapStruct Plus:
MapStruct Plus则是MapStruct的扩展或增强版本,内嵌了MapStruct,并与其完全兼容。在MapStruct的基础上进行了多项增强,提供了更高级和灵活的映射功能、更好的性能和错误处理、简化的使用体验和集成以及其他实用的增强功能。如果之前已经使用MapStruct,可以无缝替换为MapStruct Plus的依赖。
功能和特点:
- 自动生成Mapper接口:用户只需在类上添加@AutoMapper注解,并指定目标类型,MapStruct Plus即可自动生成转换逻辑。
- 增强的类型转换功能:
1.支持复杂类型转换,如嵌套对象或集合的映射。
2.提供条件映射,根据某些条件决定是否执行映射,或使用不同的映射策略。
3.允许自定义转换逻辑,以处理特殊的转换需求。
- 性能优化:MapStruct Plus可能对映射过程进行了优化,以提高大规模数据转换的性能。
- 更好的错误处理:提供更清晰的错误消息和调试信息,帮助快速定位问题。
- 与Lombok的整合:MapStruct Plus支持与Lombok的整合,方便开发者使用。
使用步骤:
- 引入MapStruct Plus依赖:在项目的pom.xml文件中添加MapStruct Plus的依赖。
io.github.linpeilie
mapstruct-plus-spring-boot-starter
1.3.5
- 配置Maven插件:配置maven-compiler-plugin插件,以启用注解处理器。
org.apache.maven.plugins
maven-compiler-plugin
3.11.0
17
17
UTF-8
io.github.linpeilie
mapstruct-plus-processor
1.3.5
org.projectlombok
lombok-mapstruct-binding
0.2.0
- 定义源对象和目标对象:
- 使用生成的Mapper接口:在需要转换对象的地方,注入Converter接口,并调用其convert方法。
示例演示:
- 定义源对象和目标对象
@Data
@AutoMapper(target = Student.class)
public class StudentBo {
private Long id;
private String name;
}
@Data
public class Student {
private Long id;
private String name;
}
- 示例代码(java代码)
@SpringBootTest
public class MapstructTest {
@Test
public void domainConvert(){
StudentBo studentBo = new StudentBo();
studentBo.setId(1l);
studentBo.setName("StudentBo转Student");
//StudentBo转Student
Student convert = MapstructUtils.convert(studentBo, Student.class);
System.out.println(convert);
}
}
//输出结果:
//Student(id=1, name=StudentBo转Student,)
这里封装了一个工具类:
public class MapstructUtil {
private static final Converter CONVERTER =SpringUtil.getBean(Converter.class);
//对象之间转换
public static V convert(T source, Class desc) {
if (ObjectUtil.isNull(source)) {
return null;
} else {
return ObjectUtil.isNull(desc) ? null : CONVERTER.convert(source, desc);
}
}
//对象之间转换
public static V convert(T source, V desc) {
if (ObjectUtil.isNull(source)) {
return null;
} else {
return ObjectUtil.isNull(desc) ? null : CONVERTER.convert(source, desc);
}
}
//集合之间转换
public static List convert(List sourceList, Class desc) {
if (ObjectUtil.isNull(sourceList)) {
return null;
} else {
return (List)(CollUtil.isEmpty(sourceList) ? CollUtil.newArrayList(new Object[0]) : CONVERTER.convert(sourceList, desc));
}
}
//map转换对象
public static T convert(Map map, Class beanClass) {
if (MapUtil.isEmpty(map)) {
return null;
} else {
return ObjectUtil.isNull(beanClass) ? null : CONVERTER.convert(map, beanClass);
}
}
}
三:更多用法
1.名称不一致:
当对象之前字段名称不一致,使用 @AutoMapping(source = "", target = "")注解
@Data
@AutoMapper(target = Student.class)
public class StudentBo {
private Long id;
//目标字段不一致情况
@AutoMapping(source = "name", target = "studentName")
private String name;
}
@Data
public class Student {
private Long id;
private String studentName;
}
- 示例代码(java代码)
@SpringBootTest
public class MapstructTest {
@Test
public void domainConvert(){
StudentBo studentBo = new StudentBo();
studentBo.setId(1l);
studentBo.setName("name转studentName");
//StudentBo转Student
Student convert = MapstructUtils.convert(studentBo, Student.class);
System.out.println(convert);
}
}
//输出结果:
//Student(id=1, studentName=name转studentName)
2. map转换对象
使用注解@AutoMapMapper
@Data
@AutoMapMapper
public class StudentBo {
private Long id;
private String name;
}
示例代码(Java代码)
@SpringBootTest
public class MapstructTest {
@Test
public void mapConvert(){
Map map = new HashMap<>();
map.put("id", 1l);
map.put("name", "map转StudentBo");
//map转对象
StudentBo convert = MapstructUtils.convert(map, StudentBo.class);
System.out.println(convert);
}
}
//输出结果:
//StudentBo(id=1, name=map转StudentBo)
3.自定义实体类中的属性转换
举例:字符串转集合例子
1.自定义一个类型转换器:
/**
* @Author
* @Description 集合转字符串转换器
* @Date
*/
@Component
public class ListToStringConverter {
public String listToString(List list) {
if (list == null || list.isEmpty()) {
return null;
}
return String.join(",", list);
}
}
/**
* @Author
* @Description 字符串转集合转换器
* @Date
*/
@Component
public class StringToListString {
public List stringToListString(String str) {
if (str == null) {
return Lists.newArrayList();
}
return Arrays.asList(str.split(","));
}
}
2.使用类型转换器
在 @AutoMapper 注解中使用 uses,且给需要转化的属性加上 @AutoMapping 注解,target 指向另一个需要转化的属性。
//StudentBo
@Data
//uses指定字符串转集合转换器。
@AutoMapper(target = Student.class,uses = StringToListString.class)
public class StudentBo {
private Long id;
private String name;
//指定目标对象->字段educationList
@AutoMapping(target = "educationList")
private String educations;
}
//Student
@Data
//uses指定集合转字符串转换器。
@AutoMapper(target = StudentBo.class, uses = ListToStringConverter.class)
public class Student {
private Long id;
private String name;
//指定源对象->字段educations
@AutoMapping(target = "educations")
private List educationList;
}
示例代码(Java代码)
@SpringBootTest
public class MapstructTest {
@Test
public void domainConvert() {
StudentBo studentBo = new StudentBo();
studentBo.setId(1l);
studentBo.setName("StudentBo转Student");
studentBo.setEducations("1,2,3");
Student convert = MapstructUtils.convert(studentBo, Student.class);
//结果:Student(id=1, name=StudentBo转Student, educationList=[1, 2, 3])
System.out.println(convert);
}
}
3.枚举转换
1.自定义枚举:
注解@AutoEnumMapper,指定需要转换字段state
@Getter
@AllArgsConstructor
@AutoEnumMapper("state")
public enum GoodsStateEnum {
ENABLED(1, "启用"),
DISABLED(0, "禁用");
private Integer state;
private String desc;
}
2.定义对象
//源对象
@Data
//转换目标对象GoodsVo
@AutoMapper(target = GoodsVo.class)
public class Goods {
//商品状态枚举
private GoodsStateEnum state;
}
//目标对象
@Data
public class GoodsVo {
private Integer state;
}
示例代码(Java代码)
@SpringBootTest
public class MapstructTest {
@Test
public void enumMapTest() {
//源对象,这里默认选择启用状态:1
Goods goods = new Goods();
goods.setState(GoodsStateEnum.ENABLED);
//转换GoodsVo对象
GoodsVo goodsVo = MapstructUtils.convert(goods, GoodsVo.class);
//结果:GoodsVo(state=1)
System.out.println(goodsVo);
}
}
(目前版本没有看到useEnums 属性,欢迎大家测试测试并留言,互相学习一下!)
- 当前特性从 1.2.2 开始支持(当需要进行枚举转换时(例如枚举转换为编码值,或者由编码转换为枚举),可以在目标枚举添加 @AutoEnumMapper 注解, 增加该注解后,在任意类型中需要转换该枚举时都可以自动转换。需要注意:当前枚举必须有一个可以保证唯一的字段,并在使用当前注解时,将该字段名,添加到注解提供的 value 属性中。枚举和使用枚举的类,要在同一个模块中。
- 该特性从 1.4.2 开始支持(跨模块支持,当枚举与要使用的类型,不在同一个模块(module)中时,并不能自动转换,需要指定依赖关系。在 AutoMapper 注解中,可以通过属性 useEnums 来指定,当前转换关系,需要依赖的枚举类列表。这些枚举需要被 AutoEnumMapper注解。需要注意:当两个类在同一个模块(module)中,无需指定,可以自动转换。当前特性主要解决跨模块之间不能自动转换的问题。
官方文档:简介 | MapStructPlus
综上所述:
MapStruct Plus在MapStruct的基础上进行了多项增强,提供了更高级和灵活的映射功能、更好的性能和错误处理、简化的使用体验和集成以及其他实用的增强功能。这使得MapStruct Plus成为处理Java对象映射的一个强大而高效的工具。