经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Java » 查看文章
麻了,不要再动不动就BeanUtil.copyProperties!!!
来源:cnblogs  作者:JAVA旭阳  时间:2023/4/17 9:17:38  对本文有异议

前言

最近项目上要求升级一个工具包hutool的版本,以解决安全漏洞问题,这不升级还好,一升级反而捅出了更大的篓子,究竟是怎么回事呢?

事件回顾

我们项目原先使用的hutool版本是5.7.2,在代码中,我们的数据传输对象DTO和数据实体对象中大量使用了工具包中的BeanUtil.copyProperties(), 大体代码如下:

  1. 数据传输对象
  1. @Data
  2. @ToString
  3. public class DiagramDTO {
  4. // 前端生产的字符串id
  5. private String id;
  6. private String code;
  7. private String name;
  8. }
  1. 数据实体对象
  1. @Data
  2. @ToString
  3. public class Diagram {
  4. private Integer id;
  5. private String code;
  6. private String name;
  7. }
  1. 业务逻辑
  1. public class BeanCopyTest {
  2. public static void main(String[] args) {
  3. // 前端传输的对象
  4. DiagramDTO diagramDTO = new DiagramDTO();
  5. // 如果前端传入的id事包含e的,升级后就会报错
  6. diagramDTO.setId("3em3dgqsgmn0");
  7. diagramDTO.setCode("d1");
  8. diagramDTO.setName("图表");
  9. Diagram diagram = new Diagram();
  10. // 关键点,数据拷贝
  11. BeanUtil.copyProperties(diagramDTO, diagram);
  12. System.out.println("数据实体对象:" + diagram);
  13. //设置id为空,自增
  14. diagram.setId(null);
  15. //保存到数据库中 TODO
  16. //diagramMapper.save(diagram);
  17. }
  18. }

升级前,hutool是5.7.2版本下,执行结果如下图。

  • BeanUtil.copyProperties虽然字段类型不一样,但是做了兼容处理,所以业务没有影响业务逻辑。

升级后,hutool是5.8.8版本,执行结果如下图所示:

  • 执行报错,因为升级后的版本修改了实现,增加了下面的逻辑,如果包含E, 就会抛错,从而影响了业务逻辑,同时这个id是否包含e又是随机因素,到了生产才发现,就悲剧了。

分析探讨

我发现大部分人写代码都喜欢偷懒,在上面的场景中,虽然BeanUtil.copyProperties用的一时爽,但有时候带来的后果是很严重的,所以很不推荐这种方式。为什么这么说呢?

比如团队中的某些人偷偷改了数据传输对象DTO,比如修改了类型、删去了某个字段。用BeanUtil.copyProperties的方式压根无法在编译阶段发现,更别提修改的影响范围了,这就只能把风险暴露到生产上去了。那有什么更好的方法呢?

推荐方案

  1. 原始的getset方式

我是比较推崇这种做法的,比如现在DiagramDTO删去某个字段,编译器就会报错,就会引起你的注意了,让问题提前暴露,无处遁形。

你可能觉得站着说话不腰疼,字段少好,如果字段很多还不得写死啊,我这里推荐一个IDEA的插件,可以帮你智能生成这样的代码。

话不多说,自己玩儿去~~

  1. 使用开源库ModelMapper

ModelMapper是一个开源库,可以很方便、简单地将对象从一种类型映射到另一种类型,底层是通过反射来自动确定对象之间的映射,还可以自定义映射规则。

  1. private static void testModelMapper() {
  2. ModelMapper modelMapper = new ModelMapper();
  3. DiagramDTO diagramDTO = new DiagramDTO();
  4. diagramDTO.setId("3em3dgqsgmn0");
  5. diagramDTO.setCode("d1");
  6. diagramDTO.setName("图表");
  7. Diagram diagram = modelMapper.map(diagramDTO, Diagram.class);
  8. }
  1. 使用开源库MapStruct

MapStruct也是Java中另外一个用于映射对象很流行的开源工具。它是在编译阶段生成对应的映射代码,相对于ModelMapper底层放射的方案,性能更好。

  1. @Mapper
  2. public interface DiagramMapper {
  3. DiagramMapper INSTANCE = Mappers.getMapper(DiagramMapper.class);
  4. DiagramDTO toDTO(Diagram diagram);
  5. Diagram toEntity(DiagramDTO diagram);
  6. }
  7. private static void testMapStruct() {
  8. DiagramDTO diagramDTO = new DiagramDTO();
  9. diagramDTO.setId("3em3dgqsgmn0");
  10. diagramDTO.setCode("d1");
  11. diagramDTO.setName("图表");
  12. Diagram diagram = DiagramMapper.INSTANCE.toEntity(diagramDTO);
  13. }
  • DiagramMapper接口使用了@Mapper注解,用来表明使用MapStruct处理
  • MapStruct中更多高级特性大家自己探索一下。

总结

小结一下,对象在不同层之间进行转换映射,很不建议使用BeanUtil.copyProperties这种方式,更加推荐使用原生的set, get方式,不容易出错。当然这不是将BeanUtil.copyProperties一棒子打死,毫无用武之地,在特定场景,比如方法内部对象的转换等影响小的范围还是很方便的,如果你有其他的想法,也可以留下你的想法,一起探讨交流。

欢迎关注个人公众号【JAVA旭阳】交流学习!!

原文链接:https://www.cnblogs.com/alvinscript/p/17322532.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号