前言
已经2024年了,你还在使用BeanUtils进行对象的赋值吗?如果你现在还在使用BeanUtils,看了本文,就会从此改用Mapstruct。
在我大学四年学习代码的过程中,在进行无数次DTO、VO和各种对象之间的值拷贝时一直在使用BeanUtils.copyProperties()
这个方法,它也确实是一个好用的方法,简化了许多代码。在进行各种实体DTO对象的拷贝时只需要加一句BeanUtils.copyProperties()
便能轻松赋值。但是在我开始实习进入工作之后发现了更为‘懒惰’的办法就可以完成对象之间的转换,那就是MapStruct
。
MapStruct简介
mapstruct是一种 实体类 映射框架,能够通过Java注解将一个实体类的属性安全地赋值给另一个实体类。有了mapstruct,只需要定义一个映射器接口,声明需要映射的方法,在编译过程中,mapstruct会自动生成该接口的实现类,实现将源对象映射到目标对象的效果。
mapstruct 相对于BeanUtils的优势在哪?
- mapstruct支持复杂属性赋值,而BeanUtils很容易因为是浅拷贝导致一系列的问题。
- mapstruct效率高,效率相当于自己手动get、set属性,而BeanUtils方法实现过程并不简单,是通过反射机制进行映射,相对于直接用get和set方法赋值,其性能开销更大。
- mapstruct支持不同字段间的赋值,通过注解实现
实体类映射框架大致有两种:一种是运行期通过java反射机制动态映射;另一种是编译期动态生成getter/setter,在运行期直接调用框架编译好的class类实现实体映射。接下来我们看看各种实体类映射框架的效率对比。
效率对比:
从这张图表可以看到,MapStruct不仅好用,并且它的效率更是‘遥遥领先’。
MapStruct使用
MapStruct依赖
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId>
<version>${mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
</dependency>
这边有几个要注意的点
- mapstruct-jdk8和mapstruct-processor的版本号必须一致。
- 如果你的项目中有用到了swagger框架的话,因为swagger里也有mapstruct,所以需要排除
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger2.version}</version>
<exclusions>
<exclusion>
<artifactId>mapstruct</artifactId>
<groupId>org.mapstruct</groupId>
</exclusion>
</exclusions>
代码实现
实体类
@Data
@TableName("user")
public class User {
/**
* 主键
*/
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
* 用户名
*/
@TableField("name")
private String name;
/**
* 邮箱
*/
@TableField("mail")
private String mail;
/**
* 0 男
1 女
2 未知
*/
@TableField("sex")
private Boolean sex;
}
DTO对象
@Data
public class UserDto {
/**
* 主键
*/
private Integer id;
/**
* 用户名
*/
private String name;
/**
* 邮箱
*/
private String mail;
/**
* 0 男
1 女
2 未知
*/
private Boolean sex;
}
Mapper接口
//mapstruct包下的mapper注解
@Mapper(componentModel = "spring")
public interface UserMapper {
UserDto mapper(User user);
}
第一种使用情况
当两个对象的属性类型和名称完全相同时,Mapstruct会自动拷贝。
UserDto userDto = UserMapper.mapper(user);
只需要如上代码就可以完成对象的赋值。
第二种使用情况
当两个对象的属性类型和名称不完全相同时,可以用@Mapping注解进行绑定。 假设我们把userdto里的name改为了nickname,接下来只需要在mapper接口修改
//mapstruct包下的mapper注解
@Mapper(componentModel = "spring")
public interface UserMapper {
@Mapping(target = "nickname", source = "name")
UserDto mapper(User user);
}
拓展
当然我们还可以对这个mapper进行扩展实现自己更多的自定义功能,比如可以利用它完成集合转换、数据源转换 等等,这些可以根据自己的需求进行拓展。
总结
从代码上我们可以看出MapStruct的使用的成本其实和BeanUtils差不多,但是MapStruct的高性能,可扩展性是远远优于BeanUtils的。在大型项目中往往存在着更多的性能和拓展性的问题,所以推荐大家学会MapStruct,告别BeanUtils。