做Java开发尤其是Spring Boot项目,几乎没人能躲开Lombok的@Data注解。写实体类、DTO、VO的时候往上一标,getter、setter、toString、equals、hashCode全自动生成,代码瞬间清爽大半,省下来的时间都能多喝两杯茶。但你可能不知道,这个每天都在敲的注解,其实是Spring项目里隐藏最深的“隐形坑”,不少人直到线上出现诡异的集合异常、实体比较错误,排查三天都找不到原因,才发现自己踩了大半年的雷。
今天我就结合自己在Spring生态实战里踩过的坑,把@Data最容易翻车的几个场景一次性讲透,帮你避开那些低级错误。
一、IDE看着完全正常,编译直接报cannot find symbol
很多人都遇到过这种离谱情况:代码里@Data标得好好的,IDEA完全不报红,鼠标点进去也能跳转到对应方法,一启动项目编译直接报错,提示找不到对应的getter/setter符号。
这根本不是你代码写错了,90%的情况都是开发环境配置没对齐,按下面几步排查就能解决:
打开IDEA的Settings → Build → Compiler → Annotation Processors,一定要勾选“Enable annotation processing”,没开这个开关,Lombok根本不会在编译阶段介入生成代码
去Plugins里确认Lombok插件已经安装并启用,很多人升级IDEA之后插件被自动禁用,直接导致注解失效
检查Maven/Gradle依赖配置,除了引入lombok依赖,一定要把它注册为注解处理器。以Maven为例,正确的依赖配置如下:
xml
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.34</version>
<scope>provided</scope>
</dependency>
特别提醒:JDK 17+版本一定要把Lombok升级到1.18.30以上的最新版,旧版本和高版本JDK存在兼容性问题,会直接导致注解完全失效。如果以上操作都做完还是报错,直接点File → Invalidate Caches and Restart清空IDEA缓存重启,大部分奇怪的问题都能解决。
二、继承场景下equals和hashCode直接“失灵”,集合操作全乱套
这是所有坑里面坑人最多的一个,没有之一。很多项目都习惯写一个通用的BaseEntity,把所有实体共有的id、createTime、updateTime抽出来,然后让业务实体去继承它。比如你随手写出这样的代码:
java
@Data
public class BaseEntity {
private Long id;
private LocalDateTime createTime;
private LocalDateTime updateTime;
}
@Data
public class Product extends BaseEntity {
private String name;
private BigDecimal price;
}
然后你写一段测试逻辑,两个id完全相同的Product对象,只是商品名称和价格不一样,你以为它们逻辑上不是同一个对象,可实际运行的时候,放到HashSet里会出现重复元素,作为HashMap的key也会查找失败。
问题的根源就藏在@Data的组合注解逻辑里:它默认自带的@EqualsAndHashCode和@ToString,默认配置都是callSuper=false,生成方法的时候只会处理当前子类自己声明的字段,完全忽略从父类继承来的所有属性。上面的Product类,Lombok生成的equals方法只会比较name和price,连父类里的id字段根本不会参与判断,完全违背我们对实体相等性的判断逻辑。
修复方案非常简单,只要在子类上显式指定调用父类的属性参与计算即可:
java
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class Product extends BaseEntity {
private String name;
private BigDecimal price;
}
如果你用的是JPA实体,更稳妥的做法是完全不直接用@Data,而是显式指定只使用业务主键id来生成equals和hashCode,避免其他字段变动导致对象哈希值异常:
java
@Getter
@Setter
@EqualsAndHashCode(of = "id")
@ToString
@Entity
public class User {
@Id
private Long id;
private String username;
private String email;
}
三、循环引用直接搞崩toString,日志打印直接栈溢出
很多人在写双向关联的业务类的时候,比如订单和用户的关联:Order类里持有User对象,User类里又持有Order集合。这时候直接在两个类上都标@Data,一旦你打印其中任意一个对象的日志,Lombok生成的toString方法就会无限递归调用,直接抛出StackOverflowError栈溢出。
示例场景代码:
java
@Data
public class User {
private Long id;
private String username;
private List<Order> orders;
}
@Data
public class Order {
private Long id;
private String orderNo;
private User user;
}
这种场景下不要无脑全字段生成toString,用@ToString.Exclude把循环引用的字段排除掉就可以完美解决:
java
@Data
public class User {
private Long id;
private String username;
@ToString.Exclude
private List<Order> orders;
}
@Data
public class Order {
private Long id;
private String orderNo;
@ToString.Exclude
private User user;
}
最后给大家总结一套Spring项目里@Data的安全使用准则
普通POJO、DTO、VO这类没有继承关系、没有双向关联的类,可以放心直接使用@Data
存在继承关系的实体类,必须显式加上@EqualsAndHashCode(callSuper = true)和@ToString(callSuper = true)
数据库映射的实体类,优先单独使用@Getter、@Setter,再自定义equals和hashCode只基于主键字段生成,不要直接用全功能的@Data
存在循环引用的类,一定要用@ToString.Exclude排除掉互相引用的字段,避免日志打印直接崩溃
很多开发者用了好几年Lombok,都没意识到@Data背后藏着这么多细节,工具越方便,我们越要搞懂它背后的运行逻辑,不然哪天线上出了诡异的问题,排查起来真的会让人怀疑人生。