告别BeanUtils!MapStruct Plus快速入门与最佳实践
文章目录
前言
一、安装
二、单个对象转换
1.指定对象映射关系
2.编写测试代码
3.运行结果
4.原理解析
三、自定义实体类属性转换
1.自定义一个类型转换器
2.使用类型转换器
3.运行结果
四、工具类
总结
前言
在开发中,我们有VO、BO、POJO常见对象类型,分别服务于不同层次和常见,不同的对象类型
需要做转换,传统的方式我们使用set/get,但是一个一个手动写太繁琐和太麻烦,所以像
apache、Spring、hutool都提供了BeaenUtils工具类,那我们为什么还要使用MapStruct Plus
呢???主要是BeanUtils底层用的是反射,性能较差,而MapStruct Plus是编译期生成的,接近原
生代码效率,你就理解MapStruct Plus性能 > BeanUtils 比它厉害 牛逼就好了。MapStruct Plus是
MapStruct的PLUS版本,兼容MapStruct,额.....没学过MapStruc,不影响。
一、安装
注意:这里跟官网有点不一样,我们使用lombok,在 Maven 配置中引入 mapstruct-plus-
processor
和 lombok-mapstruct-binding
依赖是为了解决 编译期代码生成协同问题 和 Lombok
与 MapStruct 的兼容性问题。
Lombok 和 MapStruct 均依赖编译期注解处理器生成代码。若 Lombok 未先生成 getter/setter 方
法,MapStruct 将无法识别字段导致编译失败。lombok-mapstruct-binding
协调两者的处理顺序,
确保 Lombok 优先执行
从 Lombok 1.18.16 开始,必须引入该绑定库,否则 MapStruct 无法识别 Lombok 生成的构造器或
Builder 模式(如 @Builder
注解生成的类)。
<properties>
<mapstruct-plus.version>最新版本</mapstruct-plus.version>
</properties>
<dependencies>
<dependency>
<groupId>io.github.linpeilie</groupId>
<artifactId>mapstruct-plus-spring-boot-starter</artifactId>
<version>${mapstruct-plus.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source> <!-- 这里根据自己的需要进行切换 -->
<target>1.8</target> <!-- 这里根据自己的需要进行切换 -->
<annotationProcessorPaths>
<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>${mapstruct-plus.lombok.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
二、单个对象转换
1.指定对象映射关系
在 User 或者 UserVo 上面增加注解 —— @AutoMapper,并设置 target 为对方类。
以下面代码举例,添加注解:@AutoMapper(target = UserVo.class)
User类:
@Data
@AutoMapper(target = UserVo.class)
public class User {
private String username;
private int age;
private String password;
}
UserVo类:
@Data
@AutoMapper(target = User.class)
public class UserVo {
private String username;
private int age;
private String password;
}
2.编写测试代码
@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);
// 测试
System.out.println("user.getUsername().equals(userVo.getUsername()) = " + user.getUsername().equals(userVo.getUsername()));
System.out.println("user.getAge() == userVo.getAge() = " + user.getAge() +"---" + userVo.getAge());
}
}
3.运行结果
4.原理解析
User转为UserVo主要是UserVo userVo = converter.convert(user,UserVo.class);底层其实set/get实现。
@Override
public UserVo convert(User source) {
if ( source == null ) {
return null;
}
UserVo userVo = new UserVo();
userVo.setTagList( stringToListConverter.stringToList( source.getTags() ) );
userVo.setUsername( source.getUsername() );
userVo.setAge( source.getAge() );
return userVo;
}
在target目录下的generated-source/annotations/com.taoran.mapstructplus.entity
三、自定义实体类属性转换
想起手上有一个需求,前端传身份正反面图片URL,传的是List<String>,但是数据库保存是String
并且使用,相隔保存到收据库,可以使用MapStruct Plus的自定义实体类属性转换。
有两种方式,我们讲第二种。
1、@AutoMapping expression指定表达式:适合简单。
2、自定义一个类型转换器:适合复杂。
1.自定义一个类型转换器
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 username;
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 username;
private int age;
@AutoMapping(target = "tags")
private List<String> tagList;
}
测试类:
@Test
public void test1() {
// 创建一个 User 对象
User user = new User();
user.setUsername("wen");
user.setAge(18);
user.setPassword("123456");
user.setTags("Java,Python,C++");
// 转换
UserVo userVo = converter.convert(user, UserVo.class);
System.out.println(userVo);
}
3.运行结果
四、工具类
package com.ailian.common.core.utils;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import io.github.linpeilie.Converter;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import java.util.List;
import java.util.Map;
/**
* Mapstruct 工具类
* <p>参考文档:<a href="https://mapstruct.plus/introduction/quick-start.html">mapstruct-plus</a></p>
*
*
* @author Michelle.Chung
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class MapstructUtils {
private final static Converter CONVERTER = SpringUtils.getBean(Converter.class);
/**
* 将 T 类型对象,转换为 desc 类型的对象并返回
*
* @param source 数据来源实体
* @param desc 描述对象 转换后的对象
* @return desc
*/
public static <T, V> V convert(T source, Class<V> desc) {
if (ObjectUtil.isNull(source)) {
return null;
}
if (ObjectUtil.isNull(desc)) {
return null;
}
return CONVERTER.convert(source, desc);
}
/**
* 将 T 类型对象,按照配置的映射字段规则,给 desc 类型的对象赋值并返回 desc 对象
*
* @param source 数据来源实体
* @param desc 转换后的对象
* @return desc
*/
public static <T, V> V convert(T source, V desc) {
if (ObjectUtil.isNull(source)) {
return null;
}
if (ObjectUtil.isNull(desc)) {
return null;
}
return CONVERTER.convert(source, desc);
}
/**
* 将 T 类型的集合,转换为 desc 类型的集合并返回
*
* @param sourceList 数据来源实体列表
* @param desc 描述对象 转换后的对象
* @return desc
*/
public static <T, V> List<V> convert(List<T> sourceList, Class<V> desc) {
if (ObjectUtil.isNull(sourceList)) {
return null;
}
if (CollUtil.isEmpty(sourceList)) {
return CollUtil.newArrayList();
}
return CONVERTER.convert(sourceList, desc);
}
/**
* 将 Map 转换为 beanClass 类型的集合并返回
*
* @param map 数据来源
* @param beanClass bean类
* @return bean对象
*/
public static <T> T convert(Map<String, Object> map, Class<T> beanClass) {
if (MapUtil.isEmpty(map)) {
return null;
}
if (ObjectUtil.isNull(beanClass)) {
return null;
}
return CONVERTER.convert(map, beanClass);
}
}
总结
MapStruct Plus的用法还有很多,例如:Map转对象、类型转换、一个类与多个类之间转换、类循
环嵌套、类转换API等,具体可以看官网文档。
MapStruct Plus官网:MapStructPlus
MapStruct官网:MapStruct – Java bean mappings, the easy way!