最近在做性能优化,发现之前一个后台项目的接口有一些慢,仔细看看发现代码中有拷贝对象的逻辑,而且拷贝对象用到的还是BeanUtils.copyProperties()。在阿里巴巴编码规范插件中也是不支持使用这种方法进行拷贝的,如果有使用这种方法,用编码规约扫描会直接提示“避免用Apache Beanutils进行属性的copy”。同时会说明:“说明:Apache BeanUtils性能较差,可以使用其他方案比如Spring BeanUtils, Cglib BeanCopier”。除了建议的这几种方案外,还有一个更好的方案就是今天要介绍的MapsStruct。
1.定义
什么是MapStruct?
MapStruct 是一个代码生成器,它基于约定优于配置方法,极大地简化了Java bean类型之间映射的实现。生成的映射代码使用简单的方法调用,因此速度快、类型安全且易于理解。
2.实践
2.1工程引入Maven依赖
<dependency>
<groupId>org.mapstruct</groupId>
<!-- jdk8以下就使用mapstruct -->
<artifactId>mapstruct-jdk8</artifactId>
<version>1.3.0.Final</version>
</dependency>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.3.0.Final</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
2.2定义实体类
定义2个实体类,一个是User,一个是UserDTO,目标是把User的值拷贝到UserDTO
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
/**
* 用户编号,主键
*/
private Long id;
/**
* 用户名
*/
private String name;
/**
* 电话
*/
private Long mobile;
/**
* 年龄
*/
private Integer age;
/**
* 性别
*/
private Integer sex;
/**
* 家庭住址
*/
private String address;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserDTO {
/**
* 用户编号,主键
*/
private Long id;
/**
* 用户名
*/
private String userName;
/**
* 电话
*/
private Long mobile;
/**
* 年龄
*/
private Integer age;
/**
* 性别
*/
private Integer sex;
/**
* 家庭住址
*/
private String address;
}
可以看到两个实体的字段名称不完全相同,这种情况下,不能使用BeanUtil.copyProperties()。使用MapStruct就可以处理这种情况,定义一个接口如下:
@Mapper
public interface UserConvert {
UserConvert INSTANCE = Mappers.getMapper(UserConvert.class);
/**
* 转换
* @param user
* @return
*/
@Mappings({
@Mapping(source = "name", target = "userName"),
})
UserDTO convert(User user);
}
有拷贝对象的业务场景,直接使用如下代码:
UserDTO userDTO1 = UserConvert.INSTANCE.convert(user);
原理就是通过JSR 269注解处理器在target自动生成接口定义的实现类,具体如下:
image.png
3.性能
对常用的几种方案做下性能上的对比,这样更能看出MapsStruts的效率,具体测试代码如下:
User user = new User(1L,"小王",15800001111L,25,1,"中国北京朝阳");
long timeTaken;
long time1 = System.currentTimeMillis();
UserDTO userDTO;
for (int i = 0;i<500000;i++){
userDTO = new UserDTO();
org.apache.commons.beanutils.BeanUtils.copyProperties(userDTO,user);
}
long time2 = System.currentTimeMillis();
timeTaken =time2-time1;
System.out.println("Apache Commons BeanUtils:"+timeTaken+"(ms)");
long time3 = System.currentTimeMillis();
for (int i = 0;i<500000;i++){
userDTO = new UserDTO();
BeanUtils.copyProperties(user,userDTO);
}
long time4 = System.currentTimeMillis();
timeTaken =time4-time3;
System.out.println("Spring BeanUtils:"+timeTaken+"(ms)");
long time5 = System.currentTimeMillis();
for (int i = 0;i<500000;i++){
UserDTO userDTO1 = UserConvert.INSTANCE.convert(user);
}
long time6 = System.currentTimeMillis();
timeTaken =time6-time5;
System.out.println("MapStruct:"+timeTaken+"(ms)");
long time7 = System.currentTimeMillis();
for (int i = 0;i<500000;i++){
UserDTO userDTO1 =copyField(user);
}
long time8 = System.currentTimeMillis();
timeTaken =time8-time7;
System.out.println("Getter/Setter:"+timeTaken+"(ms)");
执行结果:
Apache Commons BeanUtils:5767(ms)
Spring BeanUtils:1102(ms)
MapStruct:14(ms)
Getter/Setter:14(ms)
看到上面的结果,可以得出结论Apache Commons BeanUtils确实耗时时间长,效率低,用Spring BeanUtils效率至少可以提高4倍,用MapStruct效率可以提高40倍,甚至可以和自己Getter/Setter的效率不相上下(注:效率倍数只是本次测试的数据,理论上转换的字段越多,循环次数越多,效果越明显)。
4.总结
本文主要讲了MapStruct的使用方法以及与其它拷贝对象方法的性能比较,着重突出MapStruct处理效率。除了上述实践中的简单使用,MapStruct还有许多其它的使用方法,具体内容可以看下它的官网(https://mapstruct.org)。
总的来说MapStruct是一个功能强大的创建映射器的工具库,可以减少开发人员编写一些基本模板代码的工作量,提高编码效率。
参考资料:
1.https://mapstruct.org/
网友评论