Skip to content

第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,equalshashCode 的设计非常关键。

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 对比

维度CJava + 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 的合格使用标准不是“代码变短”,而是变短后语义仍然明确、风险仍然可控。