背景/场景
我们在编程过程中,经常存在实体类的转换。如数据库层对象pojo类 与 底层对外传输的对象 xxResponse类。一般常用的方式是每个值去get/set。幸运的是,我们有很多框架可以解决这种问题。
常见的工具类
- Spring BeanUtils
- Apache BeanUtils
- Dozer
- Orika
- MapStruct
- ModelMapper
- JMapper
那我们如何选择这些工具呢
性能对比
不啰嗦,直接上图
总之,就是 MapStruct性能在各种综合情况下最优
MapStruct使用
maven依赖引入
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.3.1.Final</version>
</dependency>
<!-- lombok插件 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
打包
<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>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.3.1.Final</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
示例代码(拷贝到IDE可直接运行)
public class Test {
public static void main(String[] args) {
UserDo userDo = new UserDo("zhangsan", 20, "酒仙桥");
UserResp userResp = SourceMapper.MAPPER.convert(userDo);
System.out.println( JSON.toJSONString(userResp));
}
}
/**
* 原始对象
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
class UserDo {
private String name;
private int age;
private String address;
}
/**
* 返回给前端的对象
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
class UserResp {
private String name;
//这里故意少掉 age属性,看缺失字段是否会成功
//这里故意写成 addr属性,测试名称不同能否转换
private String addr;
}
/**
* 转换器
*/
@Mapper
interface SourceMapper{
SourceMapper MAPPER = Mappers.getMapper( SourceMapper.class );
/**
* 对象转换时参数不对应使用 @Mapping注解 显示指定值的对应关系
* 如果存在多个可使用 @Mappings注解,里面是个集合
* 如 @Mappings(value = {
* @Mapping(source = "aa", target = "bb" ),
* @Mapping(source = "cc", target = "dd" )})
* @param userDo
* @return
*/
@Mapping( source = "address", target = "addr" )
UserResp convert( UserDo userDo );
}
代码编译后
在 target/generated-sources/annotations 目录会自动生成 SourceMapper的实现类
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2020-05-23T13:08:56+0800",
comments = "version: 1.3.1.Final, compiler: javac, environment: Java 1.8.0_60 (Oracle Corporation)"
)
class SourceMapperImpl implements SourceMapper {
@Override
public UserResp convert(UserDo userDo) {
if ( userDo == null ) {
return null;
}
UserResp userResp = new UserResp();
userResp.setAddr( userDo.getAddress() );
userResp.setName( userDo.getName() );
return userResp;
}
}
安装 MapStruct 插件
在 IDEA 中依次打开 File - > Settings - > Plugins
然后在 Markeyplace 搜索框中输入 mapstruct,点击 install,然后重启 IDE 即可,不再啰嗦啦
MapStruct技术总结
技术要领
- 需要写个接口做转换器
- 接口类上需要声明 @Mapper 注解
- 接口中需声明 SourceMapper MAPPER = Mappers.getMapper( SourceMapper.class );
- 接口中写要转换的接口:
- 参数为原始对象,
- 返回为 要返回的对象
- 接口上声明要转换的具体参数
框架优点
- 相对反射来说
- 框架使用注解生成代码,运行时比较高效。
- 反射debug困难,这里debug直接到生成的代码中了
- 编写层面来说
- 写法快捷高效
- 手写 set 方法属性太多还容易漏,这里不会
- 名字不对应恰好又不想返回的属性自动过滤,简直不要太方便了
参考
- 芋道源码mapStruct文章:http://www.iocoder.cn/Spring-Boot/MapStruct/
- 框架/工具性能对比:https://www.baeldung.com/java-performance-mapping-frameworks
- mapstruct官网:https://mapstruct.org/
- 官方文档:https://mapstruct.org/documentation/stable/reference/html/#shared-configurations
- 官方git代码示例:https://github.com/mapstruct/mapstruct-examples