Appearance
第51课:Lombok
🎯 学习目标
- 理解 Lombok 解决的问题:减少样板代码,而不是替代面向对象设计。
- 掌握
@Getter、@Setter、@ToString、@EqualsAndHashCode、@Builder、@RequiredArgsConstructor的使用场景。 - 能判断哪些注解适合 DTO,哪些注解不适合 Entity 或核心领域对象。
- 理解 Lombok 依赖编译期注解处理器,知道 IDE 和构建工具需要正确配置。
- 能识别
@Data、@Builder、继承、双向关联中的常见坑。
📖 一、为什么需要 Lombok
Java Bean 经常需要大量样板代码:
java
public class UserDTO {
private Long id;
private String name;
private String email;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}Lombok 可以把它简化为:
java
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class UserDTO {
private Long id;
private String name;
private String email;
}Lombok 的核心机制是“编译期生成代码”。源文件中看不到 getter/setter,但编译后的 .class 文件里会有。
📖 二、依赖配置
1. Maven
xml
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.34</version>
<scope>provided</scope>
</dependency>如果使用 Spring Boot,可以让 Boot 管理版本:
xml
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>2. Gradle
groovy
dependencies {
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testCompileOnly 'org.projectlombok:lombok'
testAnnotationProcessor 'org.projectlombok:lombok'
}3. IDE 配置
IntelliJ IDEA 需要开启:
text
Settings -> Build, Execution, Deployment -> Compiler -> Annotation Processors
Enable annotation processing否则代码能用 Maven 编译,但 IDE 里可能报红。
📖 三、常用注解
1. @Getter 和 @Setter
java
@Getter
@Setter
public class ProductDTO {
private Long id;
private String name;
private BigDecimal price;
}也可以只给字段加:
java
public class User {
@Getter
private Long id;
@Setter
private String nickname;
}推荐优先使用细粒度注解,而不是所有类都上 @Data。
2. @ToString
java
@ToString
public class LoginRequest {
private String username;
@ToString.Exclude
private String password;
}敏感字段必须排除。
3. @EqualsAndHashCode
java
@EqualsAndHashCode(of = "id")
public class UserDTO {
private Long id;
private String name;
}如果对象放入 HashSet 或作为 HashMap 的 key,equals 和 hashCode 的设计非常关键。
4. @NoArgsConstructor / @AllArgsConstructor
java
@NoArgsConstructor
@AllArgsConstructor
public class UserDTO {
private Long id;
private String name;
}Jackson、JPA 等框架经常需要无参构造器。
5. @RequiredArgsConstructor
适合构造器注入:
java
@Service
@RequiredArgsConstructor
public class OrderService {
private final OrderRepository orderRepository;
private final PaymentClient paymentClient;
}它会为 final 字段生成构造器。相比 @Autowired 字段注入,这种方式更容易测试,也能保证依赖不可变。
6. @Builder
java
@Builder
@Getter
public class OrderCreateCommand {
private final Long userId;
private final Long productId;
private final Integer quantity;
}使用:
java
OrderCreateCommand command = OrderCreateCommand.builder()
.userId(1L)
.productId(100L)
.quantity(2)
.build();适合参数较多的不可变对象。
📖 四、@Data 的使用边界
@Data 等价于组合:
text
@Getter
@Setter
@ToString
@EqualsAndHashCode
@RequiredArgsConstructor它很方便,但不应该无脑使用。
适合使用 @Data
java
@Data
public class UserQueryRequest {
private String keyword;
private Integer page;
private Integer size;
}简单 DTO、请求对象、响应对象可以使用。
不建议使用 @Data
java
@Data
@Entity
public class UserEntity {
@Id
private Long id;
@OneToMany(mappedBy = "user")
private List<OrderEntity> orders;
}风险包括:
text
toString 触发懒加载
双向关联导致递归
equals/hashCode 引用可变字段
实体生命周期中 ID 为空导致集合行为异常
敏感字段被打印⚠️ 五、常见陷阱
1. @Builder 和默认值
错误示例:
java
@Builder
public class PageQuery {
private Integer page = 1;
private Integer size = 20;
}通过 builder 创建对象时,字段默认值可能不生效。
正确写法:
java
@Builder
public class PageQuery {
@Builder.Default
private Integer page = 1;
@Builder.Default
private Integer size = 20;
}2. @ToString 打印敏感信息
java
@ToString
public class LoginRequest {
private String username;
@ToString.Exclude
private String password;
}密码、Token、身份证、手机号完整值都不应该进入日志。
3. 继承中的 equals/hashCode
继承类使用 @EqualsAndHashCode 时,要明确是否调用父类:
java
@EqualsAndHashCode(callSuper = true)
public class AdminUser extends BaseUser {
private String role;
}不要忽略编译器警告。
4. Lombok 不是运行时依赖
Lombok 的主要作用发生在编译期。生产运行环境不应该依赖它完成逻辑。
🆚 六、Java vs C 对比
| 维度 | C | Java + Lombok |
|---|---|---|
| 结构体访问 | 直接字段访问 | Java Bean 方法访问 |
| 样板代码 | 手写 struct 初始化/释放 | Lombok 生成 getter/setter/builder |
| 编译期扩展 | 宏、预处理器 | 注解处理器 |
| 风险 | 宏展开难调试 | 生成代码不可见,IDE/构建需配置 |
Lombok 有点像“类型安全版本的代码生成宏”,但它不是预处理文本替换,而是参与 Java 编译过程。
💡 七、最佳实践
- DTO 可以使用
@Data,Entity 谨慎使用@Data。 - Service 构造器注入优先使用
@RequiredArgsConstructor。 - 不要在核心领域对象上过度依赖 setter,能不可变就不可变。
@ToString必须排除敏感字段和双向关联字段。@EqualsAndHashCode要明确业务身份,避免包含可变集合。@Builder默认值需要@Builder.Default。- 团队必须统一 Lombok 版本和 IDE 注解处理器配置。
- 如果生成代码影响调试,可以使用 IDE 的 delombok 或查看编译后的 class 行为。
🎓 小结
Lombok 的价值是减少重复样板代码,但它不能替代清晰的对象设计。真正的重点不是“少写 getter/setter”,而是知道哪些对象应该可变、哪些字段应该参与相等性、哪些信息不能进入日志、哪些框架需要无参构造器。
企业项目中可以使用 Lombok,但要有边界:DTO 可以简化,Entity 和领域模型要谨慎。
🧭 八、团队使用规范建议
Lombok 最怕每个人按自己的习惯乱用。团队可以约定:
text
1. DTO、Request、Response 可以使用 @Data。
2. Entity 不默认使用 @Data,优先显式 @Getter/@Setter。
3. Service 构造器注入统一使用 @RequiredArgsConstructor。
4. 禁止在包含密码、Token 的对象上无脑 @ToString。
5. @EqualsAndHashCode 必须明确 of 或 callSuper。
6. @Builder 默认值必须使用 @Builder.Default。
7. 核心领域对象如果需要不可变,优先 final 字段 + 构造器。
8. 所有开发者 IDE 必须开启 annotation processing。示例规范:
java
@Getter
@Setter
public class UserEntity {
private Long id;
private String username;
@ToString.Exclude
private String passwordHash;
}java
@Data
public class UserCreateRequest {
private String username;
private String password;
}同样是 Lombok,Entity 和 Request 的风险完全不同。
🔍 九、自测问题
学习完本节后,应该能回答:
text
Lombok 是运行时生效还是编译期生效?
@Data 包含哪些注解?
为什么 Entity 不建议无脑使用 @Data?
@Builder.Default 解决什么问题?
@RequiredArgsConstructor 为什么适合构造器注入?
@ToString.Exclude 应该用于哪些字段?
继承类使用 @EqualsAndHashCode 时为什么要关注 callSuper?
IDE 报红但 Maven 能编译时,通常是什么配置问题?Lombok 的合格使用标准不是“代码变短”,而是变短后语义仍然明确、风险仍然可控。