Lombok 实战与代码简化
本文介绍 Lombok 在 Java 项目中的实战用法,覆盖 Getter、Setter、ToString、EqualsAndHashCode、构造方法注解、Data、Builder、Slf4j 和 NonNull 等常用注解。文章重点说明 Lombok 如何减少 JavaBean 样板代码,以及在实体类、DTO、日志和构建对象时的使用边界。
第一章 Lombok 学习思路
1.1 Lombok 是什么
Lombok 是一个 Java 编译期代码生成工具。你在源码中添加注解,Lombok 会在编译时生成对应方法。
比如:
@Getter生成 getter。@Setter生成 setter。@ToString生成toString。@EqualsAndHashCode生成equals和hashCode。@Builder生成建造者模式代码。@Slf4j生成日志对象。
简单理解:Lombok 是帮你少写样板代码的代码生成器。
1.2 Lombok 到底做了什么
Lombok 不是运行时框架,它在编译阶段读取注解,然后生成字节码中需要的方法。你在源代码里看不到 getter/setter,但编译后的类里有这些方法。
使用 Lombok 前:
public class User {
private Long id;
private String username;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
使用 Lombok 后:
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class User {
private Long id;
private String username;
}
1.3 引入 Lombok
Maven 依赖示例:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.32</version>
<scope>provided</scope>
</dependency>
provided 表示 Lombok 主要参与编译,不需要跟随程序一起打包运行。
1.4 IDEA 中启用 Lombok
新版 IntelliJ IDEA 通常已经内置 Lombok 支持。如果发现 @Data、@Builder 等注解没有生效,可以检查:
pom.xml是否已经导入 Lombok 依赖。- IDEA 是否开启 Annotation Processing:
Settings->Build, Execution, Deployment->Compiler->Annotation Processors。 - Maven 是否刷新成功。
第二章 Getter 和 Setter
2.1 类级别使用
@Getter、@Setter 可以写在类上,也可以写在字段上。写在类上时,会对类中的字段统一生成方法。
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class Product {
private Long id;
private String name;
private Integer stock;
}
2.2 字段级别使用
如果只想给某个字段生成 getter 或 setter,可以把注解写在字段上。
import lombok.Getter;
import lombok.Setter;
public class ProductQuery {
@Getter
@Setter
private String keyword;
@Getter
private Integer pageSize = 10;
}
2.3 禁止生成某个方法
如果某个字段不希望生成 setter,可以使用 AccessLevel.NONE:
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class Account {
private Long id;
private String username;
@Setter(AccessLevel.NONE)
private String password;
}
密码这类敏感字段,是否允许外部直接修改,要根据业务谨慎决定。
第三章 ToString
3.1 生成 toString
@ToString 用来生成 toString 方法,方便日志输出和调试。
import lombok.ToString;
@ToString
public class Order {
private Long id;
private String orderNo;
private Integer totalAmount;
}
3.2 排除敏感字段
如果对象中有密码、身份证号、手机号等敏感信息,不要直接输出:
import lombok.ToString;
@ToString
public class LoginUser {
private Long id;
private String username;
@ToString.Exclude
private String password;
}
3.3 输出父类字段
如果类存在继承关系,可以根据需要设置是否输出父类字段:
import lombok.ToString;
@ToString(callSuper = true)
public class AdminUser extends LoginUser {
private String roleName;
}
是否输出父类字段取决于日志排查需要和敏感信息控制。
第四章 EqualsAndHashCode
4.1 基础用法
@EqualsAndHashCode 用来生成 equals 和 hashCode。它常用于需要比较对象内容、放入 HashSet 或作为 HashMap key 的场景。
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@EqualsAndHashCode
public class Category {
private Long id;
private String name;
}
4.2 指定参与比较的字段
实体类是否应该用全部字段参与比较,要根据业务决定。比如数据库实体通常更关注主键 id。
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class UserEntity {
@EqualsAndHashCode.Include
private Long id;
private String username;
private String nickname;
}
4.3 使用注意
如果对象会放进 HashSet 或作为 HashMap 的 key,不要随意修改参与 hashCode 计算的字段。否则对象放进去时和查找时的哈希值不同,可能导致找不到对象。
第五章 构造方法注解
5.1 常用构造注解
| 注解 | 作用 |
|---|---|
@NoArgsConstructor | 生成无参构造 |
@AllArgsConstructor | 生成全参构造 |
@RequiredArgsConstructor | 给 final 字段和 @NonNull 字段生成构造 |
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class Address {
private String province;
private String city;
private String detail;
}
5.2 RequiredArgsConstructor 和构造器注入
在 Spring 项目中,@RequiredArgsConstructor 常用于构造器注入。
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
/**
* 根据用户编号查询用户名称。
*
* @param userId 用户编号
* @return 用户名称
*/
public String findUsername(Long userId) {
return userRepository.findUsernameById(userId);
}
}
相比字段注入,构造器注入更容易表达依赖是必需的,也更方便测试。
第六章 Data
6.1 Data 是什么
@Data 是一个组合注解,包含:
@Getter@Setter@ToString@EqualsAndHashCode@RequiredArgsConstructor
import lombok.Data;
@Data
public class Student {
private Long id;
private String name;
private Integer age;
}
6.2 Data 的风险
@Data 很省事,但不要在所有类上无脑使用。比如:
- 有敏感字段的类,
toString可能泄露信息。 - 复杂实体类,
equals和hashCode可能不应该由所有字段决定。 - 只读对象,不应该生成 setter。
- 领域模型不希望外部随意修改字段。
实际项目中更推荐按需组合 @Getter、@Setter、@ToString、@EqualsAndHashCode。
第七章 Builder
7.1 Builder 基础用法
@Builder 可以生成建造者模式代码,适合字段多、构造参数多的对象。
import lombok.Builder;
import lombok.Getter;
import lombok.ToString;
@Getter
@ToString
@Builder
public class CreateUserCommand {
private String username;
private String phone;
private String email;
private Integer age;
}
使用方式:
public class BuilderDemo {
public static void main(String[] args) {
CreateUserCommand command = CreateUserCommand.builder()
.username("wangwu")
.phone("13800000000")
.email("wangwu@example.com")
.age(20)
.build();
System.out.println(command);
}
}
7.2 Builder 默认值
如果字段有默认值,需要配合 @Builder.Default:
import lombok.Builder;
import lombok.Getter;
@Getter
@Builder
public class PageQuery {
@Builder.Default
private Integer pageNum = 1;
@Builder.Default
private Integer pageSize = 10;
private String keyword;
}
如果没有 @Builder.Default,通过 builder 创建对象时字段默认值可能不会按你预期保留。
7.3 Builder 适合的场景
适合使用:
- 创建参数很多的命令对象。
- 创建不可变对象。
- 测试中快速构造对象。
- 避免构造方法参数顺序混乱。
不适合滥用:
- 字段很少的简单对象。
- 必须强制校验构造过程的复杂对象。
- 需要非常明确构造语义的领域模型。
第八章 Slf4j
8.1 生成日志对象
@Slf4j 会生成一个名为 log 的日志对象。
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class PaymentService {
/**
* 模拟支付处理。
*
* @param orderNo 订单编号
*/
public void pay(String orderNo) {
log.info("开始处理支付,订单编号:{}", orderNo);
// 关键业务逻辑:真实项目中这里会调用第三方支付接口,并根据返回结果更新订单状态。
log.info("支付处理完成,订单编号:{}", orderNo);
}
}
8.2 日志占位符
日志中推荐使用 {} 占位符,不要用字符串拼接。
log.info("用户登录成功,userId:{}", userId);
这样日志级别关闭时,可以减少不必要的字符串拼接开销。
8.3 日志注意事项
日志中不要输出:
- 明文密码。
- 身份证号。
- 完整手机号。
- 银行卡号。
- 访问令牌。
调试方便不能优先于数据安全。
第九章 NonNull
9.1 参数非空检查
@NonNull 可以给参数或字段生成非空检查。
import lombok.NonNull;
public class RegisterService {
/**
* 注册用户。
*
* @param username 用户名,不能为空
*/
public void register(@NonNull String username) {
System.out.println("注册用户:" + username);
}
}
如果调用 register(null),会抛出空指针异常。它适合快速防御简单参数,但复杂校验还是要写清楚业务规则。
9.2 和业务校验的区别
@NonNull 只能判断是不是 null,不能判断:
- 字符串是否为空白。
- 手机号格式是否正确。
- 年龄是否在合理范围内。
- 用户名是否已经存在。
所以它适合基础防御,不适合替代业务校验。
第十章 Lombok 分层使用建议
10.1 DTO 和 VO
DTO、VO 通常用于数据传输,可以适当使用 Lombok。
import lombok.Data;
@Data
public class UserVO {
private Long id;
private String username;
private String nickname;
}
如果有敏感字段,避免直接用 @Data,或者使用 @ToString.Exclude 排除输出。
10.2 Entity
数据库实体类要谨慎使用 @Data,因为 equals、hashCode、toString 都可能带来隐患。
更稳妥的写法:
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
public class UserEntity {
private Long id;
private String username;
@ToString.Exclude
private String password;
}
10.3 Service
Service 中常用 @RequiredArgsConstructor 和 @Slf4j:
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Slf4j
@Service
@RequiredArgsConstructor
public class OrderService {
private final OrderRepository orderRepository;
/**
* 根据订单编号查询订单。
*
* @param orderNo 订单编号
* @return 订单对象
*/
public Order findByOrderNo(String orderNo) {
log.info("查询订单,orderNo:{}", orderNo);
return orderRepository.findByOrderNo(orderNo);
}
}
第十一章 使用建议和常见坑
11.1 推荐使用方式
| 建议 | 说明 |
|---|---|
| 按需使用注解 | 不要所有类都直接上 @Data |
| 敏感字段排除输出 | 密码、手机号、身份证号不要进入 toString |
| 实体类谨慎生成 equals | 数据库实体的相等规则要和业务保持一致 |
| 团队统一规范 | 明确哪些层可以用 @Data、哪些层推荐 @Getter |
| 构建环境要支持 | CI、IDEA、Maven 都要能正确处理注解 |
11.2 不建议使用 Lombok 的场景
- 团队成员不熟悉 Lombok,排查问题成本很高。
- 项目对可读源码要求极高,希望所有方法都显式写出来。
- 某些框架或构建插件与 Lombok 兼容性不好。
- 领域模型行为复杂,不希望外部随意修改字段。
这不是说 Lombok 不好,而是要根据项目阶段和团队习惯选择。
11.3 常见问题排查
如果 Lombok 注解不生效,可以检查:
- Maven 依赖是否导入成功。
- IDEA 是否开启 Annotation Processing。
- Maven 是否刷新。
- 项目 JDK 和编译插件版本是否匹配。
- CI 环境是否使用了正确的 Maven 和 JDK。
总结
Lombok 通过注解减少 getter、setter、构造方法、toString、equals、hashCode、Builder、日志对象等样板代码。
它的正确使用方式不是“所有类都加 @Data”,而是根据类的职责按需选择注解。对于 DTO、VO,可以适当简化;对于 Entity、领域模型和安全敏感对象,要更谨慎。用得好,Lombok 能让代码清爽很多;用得太随意,也会把一些本该显式表达的业务规则藏起来。