👆🏻👆🏻👆🏻关注博主,让你的代码变得更加优雅。
前言
Mapper Struct 是目前最好的 Java 对象复制的工具之一。
上一节我们讲到了 Mapper Struct 的应用,以及测试了他的性能,发现他的性能已经无限接近直接 get/set 了。
今天我们来讲一下如何集成spring 以及一些高阶用法。让你在工作中各种对象转换游刃有余。
最佳实践
直接上案例
案例地址GitHub: https://github.com/zhuangjiaju/easytools/blob/main/easytools-web/easytools-web-web/src/main/java/com/github/zhuangjiaju/easytools/web/web/contoller/mapperstruct/MapperStructWebController.java
案例地址gitee: https://gitee.com/zhuangjiaju/easytools/blob/main/easytools-web/easytools-web-web/src/main/java/com/github/zhuangjiaju/easytools/web/web/contoller/mapperstruct/MapperStructWebController.java
先看完整代码
先看我们的对象转换需求:
- 将一个 json 字符串 转成一个数组
- 将一个 用户的DTO对象转成VO对象
- 放入一个当前的日期
- 放入一个唯一id
转换前的对象:
/*** MapperStruct 对象** @author Jiaju Zhuang*/
@Data
@SuperBuilder
@AllArgsConstructor
@NoArgsConstructor
public class MapperStructDTO implements Serializable {private static final long serialVersionUID = EasyToolsConstant.SERIAL_VERSION_UID;/*** id*/private Long id;/*** 用户id的json*/private String userIdJson;/*** 创建人*/private UserDTO createUser;
}
转换后的对象:
/*** 结果查询返回** @author Jiaju Zhuang*/
@Data
@SuperBuilder
@AllArgsConstructor
@NoArgsConstructor
public class MapperStructQueryVO implements Serializable {private static final long serialVersionUID = EasyToolsConstant.SERIAL_VERSION_UID;/*** id*/private Long id;/*** 用户id的json*/private List<Long> userIdList;/*** 创建人*/private UserVO createUser;/*** 当前日期*/private Date currentDate;/*** 唯一id*/private String uuid;
}
转换器代码:
/*** 结果查询返回** @author Jiaju Zhuang*/
@Mapper(componentModel = "spring", uses = {CommonWebMapper.class}, imports = {DateUtil.class})
public abstract class MapperStructWebMapper extends BaseCommonWebMapper {/*** 转换** @param user* @return*/@Mappings({@Mapping(target = "currentDate", expression = "java(DateUtil.date())"),@Mapping(target = "userIdList", source = "userIdJson", qualifiedByName = "commonJsonString2LongList"),})public abstract MapperStructQueryVO dto2vo(MapperStructDTO user);@AfterMappingprotected void afterMapperStructQuery(@MappingTarget MapperStructQueryVO target, MapperStructDTO source) {if (target == null) {return;}target.setUuid(UUID.fastUUID().toString());}
}
转换代码:
/*** 查询一条数据** @param id* @return*/
@GetMapping("query")
public DataResult<MapperStructQueryVO> query(@RequestParam("id") Long id) {MapperStructDTO data = mapperStructDemoService.queryExistent(id);// 转换需求如下:// 1. 将一个 json 字符串 转成一个数组// 2. 将一个 用户的DTO对象转成VO对象// 3. 放入一个当前的日期// 4. 放入一个唯一idMapperStructQueryVO vo = mapperStructWebMapper.dto2vo(data);log.info("转换前:{},转换后:{}", JSON.toJSONString(data), JSON.toJSONString(vo));return DataResult.of(vo);
}
输出结果:
转换前:{"createUser":{"id":1,"name":"Jiaju Zhuang"},"id":11,"userIdJson":"[\"1\",\"2\"]"},转换后:{"createUser":{"id":1,"name":"Jiaju Zhuang"},"currentDate":"2024-07-17 22:06:23.038","id":11,"userIdList":[1,2],"uuid":"eee3d4ab-bb8c-4e4c-95c5-4317a014c31a"}
返回给前端的结果:
{"success": true,"errorCode": null,"errorMessage": null,"data": {"id": 11,"userIdList": [1,2],"createUser": {"id": 1,"name": "Jiaju Zhuang"},"currentDate": "2024-07-17T14:06:23.038+00:00","uuid": "eee3d4ab-bb8c-4e4c-95c5-4317a014c31a"},"traceId": null
}
可以看到返回给前端的已经是我们想要的结果了,接下来我们一步步分解。
怎么集成spring?
在转换器上面加入 @Mapper(componentModel = “spring”) ,并且 componentModel 设置成 spring即可。 这样子 MapperStruct
就会把这个类注入到spring容器中。
@Mapper(componentModel = "spring", uses = {CommonWebMapper.class}, imports = {DateUtil.class})
public abstract class MapperStructWebMapper extends BaseCommonWebMapper {
}
然后需要的用的地方直接注入即可,我们这里用了构造器注入,大家也可以用 @Resource 注入:
@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/web/mapper-struct")
public class MapperStructWebController {private final MapperStructWebMapper mapperStructWebMapper;
}
如何将一个 json 字符串 转成一个数组?
由于代码中经常会有这个需求,所以我们把他抽到一个通用的mapper 工具里面去。
/*** 通用的web转换器** @author Jiaju Zhuang*/@Mapper(componentModel = "spring")
public abstract class CommonWebMapper {/*** 将一个json 数组转成** @param jsonString* @return*/@Named("commonJsonString2LongList")public List<Long> commonJsonString2LongList(String jsonString) {if (StringUtils.isBlank(jsonString)) {return null;}return JSON.parseArray(jsonString, Long.class);}
}
然后在 对象转换类里面加入 uses = {CommonWebMapper.class} 引入通用工具,最后用 qualifiedByName = "
commonJsonString2LongList" 调用我们指定的方法
@Mapper(componentModel = "spring", uses = {CommonWebMapper.class}, imports = {DateUtil.class})
public abstract class MapperStructWebMapper extends BaseCommonWebMapper {/*** 转换** @param user* @return*/@Mappings({@Mapping(target = "currentDate", expression = "java(DateUtil.date())"),@Mapping(target = "userIdList", source = "userIdJson", qualifiedByName = "commonJsonString2LongList"),})public abstract MapperStructQueryVO dto2vo(MapperStructDTO user);
}
如何将一个特别常用的DTO转成VO
当然也可以用上面的方法,这里我们介绍另外一种方法,继承。我们的转换器继承了 BaseCommonWebMapper ,当 转换对象中 遇到了
UserDTO 转成 UserVO 的时候,他自己会去父类里面找有没有这个方法。
/*** 结果查询返回** @author Jiaju Zhuang*/
@Mapper(componentModel = "spring")
public abstract class BaseCommonWebMapper {/*** 用户对象转成vo** @param user* @return*/public abstract UserVO _userDto2UserVo(UserDTO user);
}
转换器中继承 BaseCommonWebMapper 即可:
@Mapper(componentModel = "spring", uses = {CommonWebMapper.class}, imports = {DateUtil.class})
public abstract class MapperStructWebMapper extends BaseCommonWebMapper {/*** 转换** @param user* @return*/@Mappings({@Mapping(target = "currentDate", expression = "java(DateUtil.date())"),@Mapping(target = "userIdList", source = "userIdJson", qualifiedByName = "commonJsonString2LongList"),})public abstract MapperStructQueryVO dto2vo(MapperStructDTO user);
}
如何在转换的时候自己写代码
这个特别简单,使用 expression 即可,这样子我们就可以自己在表达式里面写代码了。当然要注意,如果遇到了方法不在当前类,需要使用
imports = {DateUtil.class} 引入该类。
@Mapper(componentModel = "spring", uses = {CommonWebMapper.class}, imports = {DateUtil.class})
public abstract class MapperStructWebMapper extends BaseCommonWebMapper {/*** 转换** @param user* @return*/@Mappings({@Mapping(target = "currentDate", expression = "java(DateUtil.date())"),@Mapping(target = "userIdList", source = "userIdJson", qualifiedByName = "commonJsonString2LongList"),})public abstract MapperStructQueryVO dto2vo(MapperStructDTO user);
}
如何写特别复杂的逻辑代码
特别复杂的逻辑代码可以在转换器转换完以后,使用 @AfterMapping 注解,然后在这个方法里面写任何代码。 当内容转换完成以后,
MapperStruct 会自动执行这个方法。
/*** 转换器** @author Jiaju Zhuang*/
@Mapper(componentModel = "spring", uses = {CommonWebMapper.class}, imports = {DateUtil.class})
public abstract class MapperStructWebMapper extends BaseCommonWebMapper {/*** 转换** @param user* @return*/@Mappings({@Mapping(target = "currentDate", expression = "java(DateUtil.date())"),@Mapping(target = "userIdList", source = "userIdJson", qualifiedByName = "commonJsonString2LongList"),})public abstract MapperStructQueryVO dto2vo(MapperStructDTO user);@AfterMappingprotected void afterMapperStructQuery(@MappingTarget MapperStructQueryVO target, MapperStructDTO source) {if (target == null) {return;}target.setUuid(UUID.fastUUID().toString());}
}
总结
今天给大家介绍了各种高阶的使用方法,平时工作应该完全胜任了,代码会别的特别优雅,把复杂的逻辑写到转换器里面,代码会变得特别优雅。
那么问题来了,MapperStruct 究竟为什么可以做到这么高速,而且可以实现这么复杂的转换呢?
带着疑问,我们下一节再见。
写在最后
给大家推荐一个非常完整的Java项目搭建的最佳实践,也是本文的源码出处,由大厂程序员&EasyExcel作者维护。
github地址:https://github.com/zhuangjiaju/easytools
gitee地址:https://gitee.com/zhuangjiaju/easytools