MapStruct使用指南
在SpringBoot项目开发中经常会涉及到各种对象的构建,代码中可能会充斥各种对象构建的代码,有没有更加优雅的解决方案呢
代码实战
直接上例子
接口接收参数时经常涉及到请求参数对象到Bo对象的转换
// 请求参数对象如下
@Data
public class CustomRequest{
@ApiModelProperty(value = "param1", required = true)
private String param1;
@ApiModelProperty(value = "param2", required = true)
private String param2;
}
// Bo对象如下
@Data
public class CustomBo{
@ApiModelProperty(value = "param1", required = true)
private String param1;
@ApiModelProperty(value = "param2", required = true)
private String param2;
@ApiModelProperty(value = "flag", required = true)
private boolean flag;
}
// controller层接口代码为
@RestController
@Slf4j
@RequiredArgsConstructor
@RequestMapping(value = "/public/v1")
public class BusinessController implements BusinessApi {
private final BusinessService service;
@Override
@PostMapping(value = "/detail")
public Result<Vo> detail(@Valid @RequestBody CustomRequest request) {
// 构建对象的代码使得代码风格变得混乱
CustomBo bo = new CustomBo()
bo.setParam1(request.getParam1());
bo.setParam2(request.getParam2());
bo.setFlag(bo.isFlag());
return ResultUtils.success(service.detail(bo));
}
}
使用Mapstruct优化后只需要一行代码解决问题
@RestController
@Slf4j
@RequiredArgsConstructor
@RequestMapping(value = "/public/v1")
public class BusinessController implements BusinessApi {
private final BusinessService service;
@Override
@PostMapping(value = "/detail")
public Result<Vo> detail(@Valid @RequestBody CustomRequest request) {
// 只需要一行即可解决,对象属性越多效果越明显
CustomBo bo = CustomConverter.INSTANCE.buildBo(param, true);
return ResultUtils.success(service.detail(bo));
}
}
需要额外增加一个接口,指定对象转换规则
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
@Mapper
public interface CustomConverter {
CustomConverter INSTANCE = Mappers.getMapper(CustomConverter.class);
/**
* Request转换Bo对象
*
* @param param1 请求参数1
* @param param2 请求参数2
* @param flag flag
* @return bo对象
*/
@Mapping(target = "param1", source = "request.param1")
@Mapping(target = "param2", source = "request.param2")
@Mapping(target = "flag", source = "flag")
CustomBo buildBo(CustomRequest request, boolean flag);
}
框架介绍
MapStruct是一个Java注解处理器,主要用于自动生成Bean映射代码。
核心功能:
- 自动生成Bean映射代码:MapStruct可以在编译时生成源对象和目标对象之间的映射代码,无需手动编写繁琐的转换逻辑。
- 高性能:MapStruct生成的代码使用纯Java方法调用,避免了反射或类似机制,从而提高了性能。
优势:
- 简化开发:MapStruct可以自动处理Java Bean之间的映射,从而简化了开发过程,节省了时间和精力。
- 减少错误:由于MapStruct生成的代码是类型安全的,因此可以在编译时捕获到映射不完整或映射不正确等错误。
- 提高性能:与基于反射的映射框架相比,MapStruct生成的代码具有更高的性能。
常用功能:
- 自定义映射:MapStruct支持自定义映射规则,包括字段名不同、类型转换等场景。
- 集合映射:MapStruct可以轻松处理集合之间的映射,包括List、Set等。
- 枚举映射:支持枚举类型之间的映射。
- 嵌套属性映射:可以处理具有嵌套属性的Java Bean之间的映射
使用指南
基础使用
先导入依赖
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.5.3.Final</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.5.3.Final</version>
</dependency>
创建一个对象转换接口
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
@Mapper
public interface CustomConverter {
CustomConverter INSTANCE = Mappers.getMapper(CustomConverter.class);
/**
* Request转换Bo对象
*
* @param param1 请求参数1
* @param param2 请求参数2
* @param flag flag
* @return bo对象
*/
@Mapping(target = "param1", source = "request.param1")
@Mapping(target = "param2", source = "request.param2")
@Mapping(target = "flag", source = "flag")
CustomBo buildBo(CustomRequest request, boolean flag);
}
// @Mapping 用来指定属性映射规则 source指定属性值来源,target指定映射到返回对象的哪一个属性
使用时直接这样用就行
CustomBo bo = CustomConverter.INSTANCE.buildBo(param, true);
构建数据库实体
其实,我们可以发散思维,MapStruct可以使用到各种需要构建对象的场景
例如构建数据库实体对象插入DB的时候那就有一个问题需要解决,构建数据库对象实体是时涉及到Date对象的实例化,而不只是属性的赋值,MapStruct支持吗?没问题,是支持的!
数据库实体对象定义如下
/**
* 订单表
*
* @TableName order
*/
@TableName(value = "order")
@Data
public class Order implements Serializable {
/**
* 自增主键
*/
@TableId
private Long id;
/**
* 品牌
*/
private String brand;
/**
* 业务类型 例:MUSIC_VIP
*/
private String category;
/**
* 状态:INIT 初始,AUTH:授权 FINISH:部分转交易剩余解冻 CLOSE:全解冻或授权超时
*/
private String status;
/**
* 交易金额
*/
private BigDecimal payAmount;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新时间
*/
private Date updateTime;
/**
* 失效时间
*/
private Date expireTime;
private static final long serialVersionUID = 1L;
}
import com.test.enums.OrderStatusEnum;
import java.math.BigDecimal;
import java.util.Date;
@Mapper(imports = {OrderStatusEnum.class, BigDecimal.class, Date.class})
public interface OrderConvert {
/**
* 构建AuthOrder
*
* @param request 请求体
* @param resp 支付宝预授权响应信息
* @return response
*/
@Mapping(target = "category", source = "request.category")
@Mapping(target = "status", expression = "java(OrderStatusEnum.INIT.toString())")
@Mapping(target = "payAmount", expression = "java(new BigDecimal(0))")
@Mapping(target = "createTime", expression = "java(new Date())")
@Mapping(target = "updateTime", expression = "java(new Date())")
@Mapping(target = "expireTime", expression = "java(new Date())")
Order buildOrder(OrderCreateReq request);
}
// 使用时只需要这样即可
// 先引入这个组件
private final OrderConvert orderConvert;
// 使用时一行代码搞定
Order order = OrderConvert.buildOrder(request);
在构建对象过程中,要执行java代码例如实例化对象等,需要注意的点如下
-
Converter接口上需要加使用Mapper注解的imports属性,指定需要引入的类
@Mapper(imports = {OrderStatusEnum.class, BigDecimal.class, Date.class})
同时需要在import部分手动把需要的类引入 -
在编写涉及到执行java代码的属性映射规则时需要使用Mapping注解的expression属性
@Mapping(target = “expireTime”, expression = “java(new Date())”)
写法是这样expression = "java(待执行的代码)