Skip to content

Lombok — 代码简化神器

Lombok 通过注解在编译期自动生成样板代码(getter/setter/构造器/equals/hashCode 等),大幅减少冗余代码。

安装

xml
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>  <!-- 编译期依赖,不传递 -->
</dependency>

IDE 需要安装 Lombok 插件(IntelliJ IDEA 内置支持)。

核心注解

java
// @Data = @Getter + @Setter + @ToString + @EqualsAndHashCode + @RequiredArgsConstructor
@Data
public class User {
    private Long id;
    private String name;
    private String email;
    private Integer age;
}

// 等价于手写:
public class User {
    private Long id;
    // ... 其他字段

    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    // ... 所有 getter/setter

    @Override
    public String toString() { ... }

    @Override
    public boolean equals(Object o) { ... }

    @Override
    public int hashCode() { ... }

    // 无参构造器(因为没有 final 字段)
    public User() {}
}

构造器注解

java
// @NoArgsConstructor — 无参构造器
// @AllArgsConstructor — 全参构造器
// @RequiredArgsConstructor — final 字段和 @NonNull 字段的构造器

@Data
@NoArgsConstructor
@AllArgsConstructor
@RequiredArgsConstructor
public class Product {
    @NonNull
    private Long id;          // RequiredArgsConstructor 包含

    @NonNull
    private String name;      // RequiredArgsConstructor 包含

    private String description;  // RequiredArgsConstructor 不包含
    private BigDecimal price;
}

// 使用
Product p1 = new Product();                          // 无参
Product p2 = new Product(1L, "iPhone", "desc", new BigDecimal("999"));  // 全参
Product p3 = new Product(1L, "iPhone");              // Required 参数

@Builder — 建造者模式

java
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Order {
    private Long id;
    private Long userId;
    private String status;

    @Builder.Default  // 设置默认值
    private LocalDateTime createTime = LocalDateTime.now();

    @Singular  // 支持单个添加
    private List<OrderItem> items;
}

// 使用
Order order = Order.builder()
    .id(1L)
    .userId(100L)
    .status("PENDING")
    .item(new OrderItem(1L, "iPhone", 1))  // @Singular 生成 item() 方法
    .item(new OrderItem(2L, "AirPods", 2))
    .build();

// 继承时的 Builder
@Data
@EqualsAndHashCode(callSuper = true)
@SuperBuilder  // 支持继承的 Builder
public class VipUser extends User {
    private String vipLevel;
}

@Value — 不可变对象

java
// @Value = @Getter + @ToString + @EqualsAndHashCode + @AllArgsConstructor + final 字段
@Value
public class Money {
    BigDecimal amount;
    String currency;
}

// 等价于:
public final class Money {
    private final BigDecimal amount;
    private final String currency;

    public Money(BigDecimal amount, String currency) { ... }
    public BigDecimal getAmount() { return amount; }
    public String getCurrency() { return currency; }
    // equals, hashCode, toString
}

Money price = new Money(new BigDecimal("99.99"), "CNY");
// price.setAmount(...);  // 编译错误,没有 setter

@Slf4j — 日志

java
// @Slf4j 自动注入 log 字段
@Slf4j
@Service
public class UserService {

    public User getUser(Long id) {
        log.debug("查询用户, id={}", id);
        User user = userRepo.findById(id).orElse(null);
        if (user == null) {
            log.warn("用户不存在, id={}", id);
        }
        return user;
    }
}

// 等价于:
// private static final Logger log = LoggerFactory.getLogger(UserService.class);

// 其他日志注解
@Log4j2   // Log4j2
@Log      // java.util.logging
@CommonsLog  // Apache Commons Logging

@SneakyThrows — 隐藏受检异常

java
// 将受检异常包装为非受检异常(谨慎使用)
@SneakyThrows(IOException.class)
public String readFile(String path) {
    return Files.readString(Path.of(path));  // 不需要 try-catch
}

// 等价于:
public String readFile(String path) {
    try {
        return Files.readString(Path.of(path));
    } catch (IOException e) {
        throw Lombok.sneakyThrow(e);  // 绕过编译器检查
    }
}

@Cleanup — 自动关闭资源

java
// 自动调用 close() 方法
public void processFile(String path) throws IOException {
    @Cleanup InputStream in = new FileInputStream(path);
    @Cleanup OutputStream out = new FileOutputStream(path + ".bak");
    // 方法结束时自动关闭,等价于 try-with-resources
    byte[] buffer = new byte[1024];
    int len;
    while ((len = in.read(buffer)) != -1) {
        out.write(buffer, 0, len);
    }
}

与 Spring 集成的最佳实践

java
// Spring 推荐:构造器注入 + @RequiredArgsConstructor
@Service
@RequiredArgsConstructor  // 自动生成包含 final 字段的构造器
@Slf4j
public class OrderService {

    private final OrderRepository orderRepo;    // final → 构造器注入
    private final UserService userService;      // final → 构造器注入
    private final EmailService emailService;    // final → 构造器注入

    public Order createOrder(CreateOrderRequest req) {
        log.info("创建订单, userId={}", req.getUserId());
        // ...
    }
}

// DTO 使用 @Data + @Builder
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserVO {
    private Long id;
    private String name;
    private String email;

    // 从 Entity 转换
    public static UserVO from(User user) {
        return UserVO.builder()
            .id(user.getId())
            .name(user.getName())
            .email(user.getEmail())
            .build();
    }
}

注意事项

java
// ❌ @Data 用于 JPA 实体类有风险
// @EqualsAndHashCode 默认包含所有字段,可能导致懒加载问题
@Data  // 不推荐用于 JPA 实体
@Entity
public class User { ... }

// ✅ JPA 实体推荐写法
@Entity
@Getter
@Setter
@NoArgsConstructor
@EqualsAndHashCode(of = "id")  // 只用 id 比较
@ToString(exclude = {"orders"})  // 排除关联集合,避免懒加载
public class User { ... }

// ❌ @Builder 与 @Data 同时使用时,@Builder 会覆盖无参构造器
// 需要显式加 @NoArgsConstructor @AllArgsConstructor
@Data
@Builder
@NoArgsConstructor    // 必须显式添加
@AllArgsConstructor   // @Builder 需要全参构造器
public class Product { ... }

Lombok 使用建议

  • 实体类(Entity):@Getter @Setter @NoArgsConstructor @EqualsAndHashCode(of="id")
  • DTO/VO:@Data @Builder @NoArgsConstructor @AllArgsConstructor
  • Service:@RequiredArgsConstructor @Slf4j
  • 不可变值对象:@Value
  • 避免在 JPA 实体上用 @Data

系统学习 Java 生态,深入底层架构