虚惊一场!运营在群里说有人删库跑路,吓得我赶紧去查数据,才发现是有人lombok踩坑了

 

引言

今天中午正在带着耳机听着歌,遨游在代码的世界里,突然就被运营在群里给 @了,气冲冲的反问我最近有删生产的用户数据的吗?我肯定客气的回答道没有呀?生产的数据我怎么能随随便便可以删除,这可是公司的红线,再说了我也没有数据库的删除权限啊,不过查询权限还是有的。

于是我赶紧登上堡垒机,然后去生产数据库查一下数据,查了一下数据是还在的,吓死了,数据还在问题就不大了,无非就是应用程序出问题了,赶紧打开代码查看下,为什么会少了一条用户数据,看了下代码貌似没啥问题就是比较简单的一个逻辑,直接从 DB通过分页查询数据给到前端,然后前端负责展示,没有啥复杂的逻辑。

心想肯定是前端的问题,肯定是他少展示了数据,立马把问题也甩给了他,让他帮忙配合一起看看是否是前端的问题,然后自己也仔细看看代码,不到一分钟前端说他展示的数据没有问题,都是后端给到的,没有漏掉展示的。那就是后端的 bug了罗。

肉眼望去觉得可能出问题的就是分页导致的数据丢了。不过这个分页插件是全公司都在用,应该不至于出问题把,找不到问题只能让测试帮忙在测试环境试试,看看是否可以复现。

测试环境复现

仔细看了一眼,居然有个去重的方法,去重逻辑也比较简单就是把 list通过转为set去下重,看下来应该就是这个去重方法有问题了 大致写了单元测试模仿了下生产的数据,大致逻辑如下:

public static void main(String[] args) {
        Set userSet = new HashSet<>();
        UserDTO userDTO = new UserDTO();
        userDTO.setId(1);
        userDTO.setUserName("java金融");
        UserDTO userDTO1 = new UserDTO();
        userDTO1.setId(2);
        userDTO1.setUserName("java金融");
        userSet.add(userDTO);
        userSet.add(userDTO1);
        System.out.println(userSet.size());
        System.out.println(userDTO1.equals(userDTO));
    }
    @Data
    static class UserDTO extends BaseDTO {
        private String userName;
    }
    @Data
    static class BaseDTO {
        private Integer id;
    }

我们可以输出结果 set集合的长度是1, user1user2 是相等的,明明两个 userID是不一样的,为何会相等,我们知道set可以去重 是因为Set的操作,都是通过操作map来实现的, setadd其实就是调用 mapput方法, mapput方法我相信大家应该都去看过其源码,这里就不详细再说了,大概流程就是通过key通过hash算法定位到数组的下标,先判断 keyhash是否相等,如果相等再去判断key的value相等,如果都相等就会覆盖原来的值。我们上面这个例子就是对象的 hashvalue都相等导致,但是我们的两个对象 user1user2 应该是不等的,因为ID不等,那为什么会相等列?我们仔细看下上面的代码,我们使用了 lombok里面 @Data注解,我们可以看看这个注解帮我们生成了哪些方法 在这里插入图片描述 在这里插入图片描述 通过上面的对比我们可以看出 @Data注解帮我们生成了 注在类上,提供类的 get、set、equals、hashCode、canEqual、toString方法,这个注解确实比较方便。上面那个 bug 就是因为它生成的equals方法有问题,我们可以把上述代码编译下,然后把 class 里面生成的 equals方法拷贝出来看看 在这里插入图片描述

通过上述生成的代码我们可以看出 equals方法只比较了 userName这个字段,也就是当前类的字段,并没有去比较父类的字段,这就是导致两个对象相等的原因,我们既然找到问题了,那解决问题就比较简单。

解决问题

  • 手动重写 equalshashCode方法,这种方法肯定是不推荐的,我们既然用了 lombok就是为了解放我们的双手,是代码变得更加简洁。
  • 在比较的类上加上 @EqualsAndHashCode(callSuper = true) callSuper = true 会包含父类的 equalshashCode方法

我们可以对比下加上 @EqualsAndHashCode(callSuper = true)和没有加上这个注解生成的 equals方法的代码差异。 在这里插入图片描述 差异点还是很明显的,加入了 @EqualsAndHashCode(callSuper = true) 会去调用父类的 equals方法比较,所以这个注解也能够解决这个问题。

  • 这样加上注解确实可以解决问题,但是每个类上面都要加上这个注解,这也是个体力活。我们可以再找找其他的方案,例如有没有比如配置文件设置下什么的,然后就能全局生效了。最终通过查询资料发现我们我们写一个 lombok.config的配置文件放在我们项目的根目录下面,内容写上 lombok.equalsAndHashCode.callSuper = call效果等同于 @EqualsAndHashCode(callSuper = true),这样的话我们就不需要为每个类都去加上这个注释了,相当于在这个项目下面只要用到了 @Data注解的类都会为其加上 @EqualsAndHashCode(callSuper = true)通过配置文件的方式就可以全局生效。

总结

  • 我们再来回顾下上面的问题,归根结底还是由于对象的 equals方法使用不当引起的,所以我们在如果在判断自定义对象业务判断相等的时候需要去重写下 hashCodeequals方法,重写的时候我们可以通过 idea来生成,生成后最好还是去看一眼,看看生成的是否符合我们的业务要求。
  • 我们在工作中操作一些常见的容器类比如Set、Map等来实现一些我们自己的业务,我们还是有必要去看看它们的源码的,就比如我们通过Set来进行去重,如果我们是使用的自定义对象的话,如果没有重写 hashCodeequals方法的话,去重就会去不成功,我们只有了解了它,才能真正的去用好它。在关于 hashCodeequals 阿里巴巴开发手册也有明确的说到

在这里插入图片描述

  • lombok 用起来还是挺爽的,但是还是有一些细节需要稍微注意下。使用前可以大概的去看看它的官网提供的内容,不然出现莫名其妙的问题你都不知道如何下手。这个就有点类似于我们使用 SpringBoot一样,用起来非常爽,但是如果遇到莫名其妙的 bug解决起来就比较头疼。
  • 最后我们再来回顾几道关于 hashCodeequals的比较常见的面试题?其实如果我们只要真正看过HashMap的源码的话,这下面几个面试题还是非常简单的。

什么情况下需要我们去重写 方法? 如果只重写 equals方法不重写 HashCode可以吗? equals ,== 和hashcode()的区别?

最后

一直想整理出一份完美的面试宝典,但是时间上一直腾不开,这套 一千多道面试题宝典,结合今年金三银四各种大厂面试题,以及 GitHub 上 star 数超 30K+ 的文档整理出来的,我上传以后,毫无意外的短短半个小时点赞量就达到了 13k,说实话还是有点不可思议的。

一千道互联网 Java 工程师面试题

内容涵盖:Java、MyBatis、ZooKeeper、Dubbo、Elasticsearch、Memcached、Redis、MySQL、Spring、SpringBoot、SpringCloud、RabbitMQ、Kafka、Linux等技术栈(485页)

《Java核心知识点合集(283页)》

内容涵盖:Java基础、JVM、高并发、多线程、分布式、设计模式、Spring全家桶、Java、MyBatis、ZooKeeper、Dubbo、Elasticsearch、Memcached、MongoDB、Redis、MySQL、RabbitMQ、Kafka、Linux、Netty、Tomcat、数据库、云计算等

《Java中高级核心知识点合集(524页)》

《Java高级架构知识点整理》

 

 由于篇幅限制,详解资料太全面,细节内容太多,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!

需要的小伙伴,可以一键三连, 点击这里获取免费领取方式


请使用浏览器的分享功能分享到微信等