四时宝库

程序员的知识宝库

优雅的转换工具MapStruct plus,纵享丝滑!


介绍:

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
}

缺点:

  1. 需要定义映射器接口:虽然这有助于明确映射规则,但也增加了额外的代码量。
  2. 对于非常简单的映射场景,可能显得过于重量级。

下面介绍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对象映射的一个强大而高效的工具。

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言
    友情链接