java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > MapStruct Plus使用

MapStruct Plus的使用教程

作者:使用

MapStruct和MapStructPlus是Java类型映射的工具,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

前言

Mapstruct 是一个代码生成器,基于约定优于配置的方法,极大简化了 Java bean 类型之间映射的实现,特点:速度快、类型安全且易于理解。

Mapstruct Plus 是 MapStruct 的增强工具(类似于 Mybatis 和 Mybatis Plus 的关系),其在 MapStruct 的基础上,实现了自动生成 Mapper 接口的功能,并强化了部分功能,使 Java 类型转换更便捷、优雅。

MapStruct Plus 内嵌 MapStruct,和 MapStruct 完全兼容,如果之前已经使用 MapStruct,可以无缝替换依赖。
参考网站:
MapStruct 官网
MapStruct Plus 官网

一、为什么要用 MapStruct(背景)

目前的系统开发中,对象模型之间需要相互转换,比如一个 User 对象需要转换为 UserVo 对象:

@Data
public class User {
    private String name;
    private int age;
    private String password;
}
@Data
public class UserVo {
    private String name;
    private int age;
}

常规的有两种方式:

// 创建一个 User 对象
User user = new User();
user.setName("wen");
user.setAge(18);
user.setPassword("123456");
// 创建一个 UserVo 对象
UserVo userVo = new UserVo();
// 一行代码实现 user => userVo
BeanUtils.copyProperties(user, userVo);

所以 MapStruct 应运而生,这个框架是基于 Java 注释处理器,定义一个转换接口,在编译的时候会根据接口类和方法相关的注解,自动生成实现类,底层是基于 getter 和 setter 方法的,比 BeanUtils 的性能要高。然而美中不足的是,当需要转换的对象较多或者结构复杂的时候,需要定义较多的转换接口和转换方法。

此时,就可以使用 MapStruct Plus ,一个注解就可以生成两个类之间的转换接口,使 Java 类型转换更加便捷和优雅。

二、MapStruct Plus 的快速开始

本文以 Spring Boot 项目为例,版本:
Spring Boot:3.3.2
JDK:17
Lombok:1.18.34

1. 引入依赖

引入 mapstruct-plus-spring-boot-starter 依赖

<dependency>
    <groupId>io.github.linpeilie</groupId>
    <artifactId>mapstruct-plus-spring-boot-starter</artifactId>
    <version>1.4.3</version>
</dependency>

引入 Maven 插件,配置项目的构建过程(这一步非常非常重要!!!)引入 Maven 插件,配置项目的构建过程(这一步非常非常重要!!!)引入 Maven 插件,配置项目的构建过程(这一步非常非常重要!!!)

	<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.projectlombok</groupId>
							<artifactId>lombok</artifactId>
							<version>${lombok.version}</version>
						</path>
						<path>
							<groupId>io.github.linpeilie</groupId>
							<artifactId>mapstruct-plus-processor</artifactId>
							<version>${mapstruct-plus.version}</version>
						</path>
						<path>
							<groupId>org.projectlombok</groupId>
							<artifactId>lombok-mapstruct-binding</artifactId>
							<version>0.2.0</version>
						</path>
					</annotationProcessorPaths>
				</configuration>
			</plugin>
		</plugins>
	</build>

最新版本依赖可以查看:MapStruct Plus 的 Maven 仓库地址

2. 指定对象映射关系

在 User 或者 UserVo 上面增加注解 —— @AutoMapper,并设置 target 为对方类。
以下面代码举例,添加注解:@AutoMapper(target = UserVo.class)

@Data
@AutoMapper(target = UserVo.class)
public class User {
    private String username;
    private int age;
    private String password;
}
@Data
@AutoMapper(target = UserVo.class)
public class User {
    private String username;
    private int age;
    private String password;
}

3. 编写测试代码

@SpringBootTest
public class QuickStartTest {

    @Autowired
    private Converter converter;

    @Test
    public void test() {
        // 创建 User 对象
        User user = new User();
        user.setUsername("wen");
        user.setAge(18);
        user.setPassword("123456");

        // 使用 MapStruct plus 进行对象间转换:User =》 UserVo
        UserVo userVo = converter.convert(user, UserVo.class);
        // 输出转换之后的对象
        System.out.println(userVo);
        // 断言测试
        assert user.getUsername().equals(userVo.getUsername());
        assert user.getAge() == userVo.getAge();
    }

}

4. 运行结果

测试通过,输出:

在这里插入图片描述

5. 原理解析

通过以上示例可以看出,User 对象转化为 UserVo 对象主要是UserVo userVo = converter.convert(user, UserVo.class);这行代码,其底层也很简单,原理是通过 getter 和 setter 实现的:

public UserVo convert(User arg0) {
        if ( arg0 == null ) {
            return null;
        }

        UserVo userVo = new UserVo();

        userVo.setUsername( arg0.getUsername() );
        userVo.setAge( arg0.getAge() );

        return userVo;
    }

该代码被保存在 target 包中,具体路径:target/generated-sources/annotations/实体类存放路径

在这里插入图片描述

通过上图,可以看到,哪怕没有给 UserVo 实体类使用@AutoMapper注解,MapStruct Plus 会自动生成 User 转 UserVo 的接口和实现类,同时也会生成 UserVo 转换为 User 的实体类和接口。

以上为重要规则,下面也能用得到!!!

三、自定义实体类中的属性转换

在上面的例子中,两个实体类中对应的属性都是同一种类型,那么想要自定义属性比如:后端存储的是字符串 String 类型的属性,想给前端返回一个 List 类型的属性,可以根据规则进行转换。

下面的举例是 String 属性和 List 属性之间的相互转化(String 《===》List)

有两种方式:

1. 自定义一个类型转换器

首先定义两个类型转换器,一个是 String 转为 List,一个是 List 是 String。且两个类型转换器需要定义为 Spring 的 Bean,即使用 @Component 注解。
String 转为 List 的转换器:

@Component
public class StringToListConverter {

    public List<String> stringToList(String str) {
        if (str == null) {
            return Collections.emptyList();
        }
        return Arrays.asList(str.split(","));
    }

}

List 转为 String 的转换器:

@Component
public class ListToStringConverter {

    public String listToString(List<String> list) {
        if (list == null || list.isEmpty()) {
            return null;
        }
        return String.join(",", list);
    }

}

2. 使用类型转换器

第二步,使用该类型转换器,即在 @AutoMapper 注解中使用 uses,且给需要转化的属性加上 @AutoMapping 注解,target 指向另一个需要转化的属性。
User 类:

@Data
@AutoMapper(target = UserVo.class, uses = StringToListConverter.class)
public class User {
    private String name;
    private int age;
    private String password;
    @AutoMapping(target = "tagList")
    private String tags;
}

UserVo 类:

@Data
@AutoMapper(target = User.class, uses = ListToStringConverter.class)
public class UserVo {
    private String name;
    private int age;
    @AutoMapping(target = "tags")
    private List<String> tagList;
}

3. 进行测试

第三步,进行测试。

@SpringBootTest
public class QuickStartTest {

    @Autowired
    private Converter converter;

    @Test
    public void test() {
        // 创建一个 User 对象
        User user = new User();
        user.setName("wen");
        user.setAge(18);
        user.setPassword("123456");
        user.setTags("Java,Python,C++");

        // 转换
        UserVo userVo = converter.convert(user, UserVo.class);
        System.out.println(userVo);
        assert userVo.getTagList().size() == 3;
    }

}

测试结果:
测试用例通过,User 类中的 String 类型的 tags 属性,成功转化为 UserVo 类中的 List 类型的 tagList 属性。

在这里插入图片描述

还有一种方法是直接在注解中写表达式,但是博主觉得这种方式没有自定义转换器好,所以在本文中不列举
如果感兴趣,详情请参考:表达式自定义属性转换

四、Map 转为 Object

MapStruct Plus 提供了 Map<String, Object> 转化为对象的功能。

转换逻辑:针对目标类中的一个属性,首先会判断 Map 中是否存在该键,如果存在的话,首先判断类型,

MapStruct Plus 在 1.4.0+ 版本取消了内置 Hutool 框架,如果需要用到 Map 转化为对象的功能时,需要引入 hutool-core 这个依赖,最新版本查看:Hutool 依赖库

<dependency>
   <groupId>cn.hutool</groupId>
   <artifactId>hutool-core</artifactId>
   <version>5.8.29</version>
</dependency>

1. 使用步骤

2. 定义对象

为了更好的理解,直接用最复杂的 Map 转对象的例子举例,即内部属性既有基本类型,也有自定义的对象

定义一个 Body 类,里面有身高体重属性,定义一个 Person 类,里面有基本信息和一个 Body 类型的属性。

Body 类:

@Data
@AutoMapMapper
public class Body {
    private int height;
    private int weight;
}

Person 类:

@Data
@AutoMapMapper
public class Person {
    private String name;
    private Integer age;
    private Body body;
}

3. 转换测试

@SpringBootTest
public class MapToObjectTest {

    @Autowired
    private Converter converter;

    @Test
    public void test() {
        // 创建一个 Map,键是 Body 的属性名,值是属性值
        Map<String, Object> map1 = new HashMap<>();
        map1.put("height", 180);
        map1.put("weight", 150);

        // 创建第二个 Map,键是 Person 的属性名,值是属性值
        Map<String, Object> map2 = new HashMap<>();
        map2.put("name", "wen");
        map2.put("age", 18);
        map2.put("body", map1);

        // 将 Map2 转化为 Person 对象
        Person person = converter.convert(map2, Person.class);
        System.out.println(person);
    }

}

测试成功,Map 对象成功转化为 Person 对象:

在这里插入图片描述

五、枚举类型转换

枚举类型的转换,需要在枚举类上添加 @AutoEnumMapper 注解,增加该注解后,在任意类型中需要转换该枚举时都可以自动转换。
使用 @AutoEnumMapper 注解的时候,需要注意:这个枚举类必须要有一个可以保证唯一的字段,并将该字段添加到注解的 value属性中

1. 定义一个枚举类

定义一个状态枚举类,唯一字段是 code,用来表示开始还是关闭:

@Getter
@AllArgsConstructor
@AutoEnumMapper("code")
public enum StateEnum {
    ENABLE(1, "启用"),
    DISABLE(0, "禁用");
    private final int code;
    private final String desc;
}

2. 定义要转换的对象

定义一个保存枚举类的类 Course,再定义一个需要转换的 CourseVo 类:

Course 类:

@Data
@AutoMapper(target = CourseVo.class)
public class Course {

    private StateEnum state;

}

CourseVo 类:

@Data
public class CourseVo {

    private Integer state;

}

3. 转换测试

@SpringBootTest
public class EnumToValueTest {

    @Autowired
    private Converter converter;

    @Test
    public void test() {
        // 创建一个 Course 对象
        Course course = new Course();
        course.setState(StateEnum.ENABLE);

        // 将 Course 对象转换为 CourseVo 对象
        CourseVo courseVo = converter.convert(course, CourseVo.class);
        System.out.println(courseVo);

        // 将 CourseVo 对象转换为 Course 对象
        Course course1 = converter.convert(courseVo, Course.class);
        System.out.println(course1);
    }

}

测试成功,Enum 可以转化为整形,整形也可以转化为 Enum:

在这里插入图片描述

4. 注意

枚举和使用枚举的类需要在同一个模块(module)中。

当枚举与要使用的类型,不在同一个模块中,是不能自动转换的,需要指定依赖关系。在 @AutoMapper 注解中,可以通过 useEnums 来指定需要依赖的枚举类列表。

六、一个类与多个类之间的转换

MapStruct Plus 还支持一个类和多个类进行转换,可以通过 @AutoMappers 来配置,该注解支持配置多个 @AutoMapper

在配置多个类进行转化的时候,多个类可能有相同的属性,为了解决属性冲突的问题,可以使用 @AutoMappings 指定多个转换规则,并且在使用 @AutoMapping 注解时,配置 targetClass 属性,指定当前规则的目标转化类。

如果配置 @AutoMapping 注解时,没有指定 targetClass,那么当前规则就会应用所有类转换。

1. 定义对象

定义一个 User 类,一个 Course 类,一个 UserVo 类。其中 UserVo 类将与 User 类和 Course 类互相映射(UserVo 《===》User、Course)。User 类和 Course 类都有 name 属性,但是只将 User 类中的 name 属性映射。

User 类:

@Data
@AutoMapper(target = UserVo.class, uses = StringToListConverter.class)
public class User {

    private String name;

    private int age;

    private String password;

    @AutoMapping(target = "tagList")
    private String tags;
}

Course 类:

@Data
@AutoMapper(target = UserVo.class)
public class Course {

    @AutoMapping(targetClass = UserVo.class, ignore = true) // 忽略 UserVo 中的 name 属性
    private String name;

    private String teacher;

}

UserVo 类:

@Data
@AutoMappers({
        @AutoMapper(target = User.class, uses = ListToStringConverter.class),
        @AutoMapper(target = Course.class)
})
public class UserVo {

    @AutoMappings({
            @AutoMapping(targetClass = User.class),
            @AutoMapping(targetClass = Course.class, ignore = true)
    })
    private String name;

    private int age;

    @AutoMapping(targetClass = User.class, target = "tags")
    private List<String> tagList;

    private String teacher;
}

2. 转换测试

@SpringBootTest
public class OneToOthersTest {

    @Autowired
    private Converter converter;

    @Test
    public void test() {
        // 创建 User 对象
        User user = new User();
        user.setName("wen");
        user.setAge(18);
        user.setPassword("123456");
        user.setTags("Java,Python,Go,C++");

        // 创建 Course 对象
        Course course = new Course();
        course.setName("Java 开发");
        course.setTeacher("教 Java 的老师");

        // 转换(User 对象和 Course 对象)为 UserVo 对象
        UserVo userVo = converter.convert(user, UserVo.class);
        userVo = converter.convert(course, userVo);
        System.out.println(userVo);

        // 转换 UserVo 对象为(User 对象和 Course 对象)
        user = converter.convert(userVo, User.class);
        course = converter.convert(userVo, Course.class);
        System.out.println(user);
        System.out.println(course);
    }

}

3. 测试结果

在这里插入图片描述

总结

本文使用大量示例详细解释了在 Spring Boot 项目开发中使用 MapStruct Plus 的方法,多加练习熟能生巧。技术没有高低之分,不管是使用原始的 getter/setter 方法,还是使用 BeanUtils,亦或者使用本文所介绍的 MapStruct Plus,只要找到解决问题的合适方案就可以。

到此这篇关于MapStruct Plus的使用教程的文章就介绍到这了,更多相关MapStruct Plus内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文