Lombok 速查指南:从基础注解到避坑实录

在 Java 工程化开发中,Lombok 几乎是标配工具。它通过编译时字节码修改(JSR 269 Pluggable Annotation Processing API),极大地精简了样板代码(Boilerplate Code)。
然而,很多同学仅停留在 @Data 一把梭的阶段。因 Lombok 误用导致的空指针、死循环和 JPA 性能问题很常见。本文不仅是一份速查表,更是基于实战经验的避坑指南。
速查
- [Lombok 速查指南:从基础注解到避坑实录](#Lombok 速查指南:从基础注解到避坑实录)
-
- [1. 细粒度控制:字段与方法](#1. 细粒度控制:字段与方法)
-
- [@Getter / @Setter](#@Getter / @Setter)
- [2. 构造器家族:初始化策略](#2. 构造器家族:初始化策略)
- [3. 核心组合注解:@Data 的爱与恨](#3. 核心组合注解:@Data 的爱与恨)
- [4. 日志工具](#4. 日志工具)
- [5. 进阶:不可变对象与构建器](#5. 进阶:不可变对象与构建器)
- 总结与决策图谱
1. 细粒度控制:字段与方法
这部分注解主要用于精准控制类成员的访问能力。
@Getter / @Setter
- 作用范围:类、字段。
- 生成的代码:标准的 getter/setter 方法。
- 架构师提示 :
- 类级别:为所有非静态字段生成方法。
- 字段级别:仅针对特定字段。
- 访问控制 :可以使用
AccessLevel参数控制生成方法的可见性。例如@Setter(AccessLevel.PROTECTED),这在设计领域模型(Domain Model)时非常有用,防止外部随意修改内部状态。
java
public class User {
@Getter @Setter
private String name;
@Setter(AccessLevel.NONE) // 不生成 Setter,保证 id 不可变
private Long id;
}
2. 构造器家族:初始化策略
Spring 4.3+ 推荐使用构造器注入,Lombok 的构造器注解配合 Spring 使用简直是天作之合。
@NoArgsConstructor
- 作用:生成无参构造器。
- 必要性:在使用 JPA(Hibernate)或 Jackson 反序列化时,通常强制要求类必须有一个无参构造器,否则会报错。
@AllArgsConstructor
- 作用 :生成包含类中所有字段的构造器。
@RequiredArgsConstructor (推荐)
- 作用 :生成包含特定字段的构造器。
final修饰的未初始化字段。- 被
@NonNull注解修饰的字段。
- 最佳实践 :替代
@Autowired。在 Spring Bean 中,使用此注解配合final字段,可以实现优雅的构造器注入,无需写臃肿的构造函数。
java
@Service
@RequiredArgsConstructor // 自动生成包含 userMapper 的构造器
public class UserService {
private final UserMapper userMapper; // Spring 会自动注入
}
3. 核心组合注解:@Data 的爱与恨
这是 Lombok 最强大但也最危险的区域。
@ToString
- 作用 :生成
toString()方法,默认包含所有非静态字段。 - 避坑 :
- 循环引用 :如果 A 类引用 B,B 类引用 A,直接用
@ToString会导致 StackOverflowError。必须使用@ToString.Exclude排除掉造成循环引用的字段。
- 循环引用 :如果 A 类引用 B,B 类引用 A,直接用
@EqualsAndHashCode
- 作用 :生成
hashCode()和equals()方法。 - 避坑 :
- 继承陷阱 :如果类有父类,默认
callSuper = false,即比较时忽略父类字段。这可能导致两个逻辑上不同的对象被判定为相等。建议显式声明@EqualsAndHashCode(callSuper = true)。
- 继承陷阱 :如果类有父类,默认
@Data
- 作用 :这是一个"全家桶"注解。
- 相当于:
@Getter+@Setter+@RequiredArgsConstructor+@ToString+@EqualsAndHashCode。
- 相当于:
- 注意 :对于
final字段,@Data不会生成 Setter。 - 严重警告 :严禁在 JPA/Hibernate 的 Entity 类上使用 @Data 。
- 原因 1:
toString可能触发懒加载(Lazy Loading),导致严重的性能问题(N+1)或LazyInitializationException。 - 原因 2:
hashCode可能包含可变字段,导致对象在 Set 或 Map 中丢失。
- 原因 1:
4. 日志工具
@Slf4j
-
作用:生成日志静态常量。
-
等价代码:
javaprivate static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(TargetClass.class); -
优势:无需手动输入类名,避免复制粘贴代码时忘记修改 Logger 类名的问题。
5. 进阶:不可变对象与构建器
虽然提问中未提及,但以下两个注解在构建健壮系统时至关重要。
@Builder (强烈推荐)
- 作用:实现建造者模式(Builder Pattern)。
- 场景 :当对象字段较多(超过 4 个)时,使用构造器非常容易出错(参数顺序搞混)。
@Builder提供了链式调用的能力,代码可读性极高。
@Value
- 作用 :
@Data的不可变版本。 - 效果 :所有字段默认
private final,不生成 Setter,类本身也是final。这非常适合用于定义 DTO(数据传输对象)或值对象(Value Object)。
总结与决策图谱
为了方便记忆,我整理了以下决策逻辑:

核心原则:Lombok 是为了让代码更清晰,而不是让逻辑更隐晦。如果你发现 Lombok 的某个注解让你失去了对代码的控制权(比如 JPA 中的 equals/hashCode 问题),请果断切回手动编写。
现在,去检查一下你的 JPA 实体类,看看是不是误用了 @Data?