1.前言
Lombok
是一个 Java 库,可以通过注解自动生成常见的 Java 代码,如 getter、setter、构造函数、toString、equals 和 hashCode 方法。使用 Lombok 可以显著减少样板代码,提高代码的可读性和可维护性。尤其对于简单的Java模型对象(POJO),简直是一款开发中的神兵利器。
因为它所有的增强都是通过注解(编译期注解
)实现,所以了解其使用主要了解一下注解即可。
2.Lombok 的安装
2.1.Maven 或 Gradle 配置
首先,你需要在项目中添加 Lombok 依赖。
Maven下的依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
<version>1.18.4</version>
</dependency>
Gradle下 的依赖(kts)
dependencies {
//*Lombok*
// <p>注解处理将不再compile classpath中,需要手动添加到annotation processor path</p>
compileOnly("org.projectlombok:lombok:1.18.4")
annotationProcessor("org.projectlombok:lombok:1.18.4")
testCompileOnly("org.projectlombok:lombok:1.18.4")
testAnnotationProcessor("org.projectlombok:lombok:1.18.4")
}
2.2. IDE 插件
Lombok 需要 IDE 支持,安装对应的插件:
- IntelliJ IDEA:通过插件市场安装 Lombok Plugin。
- Eclipse:下载 Lombok 的 JAR 文件并运行安装。
2. 常用注解
2.1 @Getter 和 @Setter
作用:自动生成 getter 和 setter 方法。
示例:
@Getter
@Setter
public class User {
private String name;
private int age;
}
说明:
可以在类级别或字段级别使用。
支持设置访问权限(如 @Getter(AccessLevel.PROTECTED))。
2.2 @ToString
作用:自动生成 toString() 方法。
示例:
@ToString
public class User {
private String name;
private int age;
}
说明:
支持排除字段(如 @ToString(exclude = “age”))。
支持调用父类的 toString() 方法(如 @ToString(callSuper = true))。
2.3 @EqualsAndHashCode
作用:自动生成 equals() 和 hashCode() 方法。
示例:
复制
@EqualsAndHashCode
public class User {
private String name;
private int age;
}
说明:
- 支持排除字段(如 @EqualsAndHashCode(exclude = “age”))。
- 支持调用父类的 equals() 和 hashCode() 方法(如 @EqualsAndHashCode(callSuper = true))。
2.4 @NoArgsConstructor
作用:生成无参构造函数。
示例:
复制
@NoArgsConstructor
public class User {
private String name;
private int age;
}
2.5 @AllArgsConstructor
作用:生成全参构造函数。
示例:
@AllArgsConstructor
public class User {
private String name;
private int age;
}
2.6 @RequiredArgsConstructor
作用:生成包含 final 字段或 @NonNull 字段的构造函数。
示例:
@RequiredArgsConstructor
public class User {
private final String name;
private int age;
}
2.7 @Data
作用:组合注解,包含 @Getter、@Setter、@ToString、@EqualsAndHashCode 和 @RequiredArgsConstructor。
示例:
复制
@Data
public class User {
private String name;
private int age;
}
2.8 @Builder
作用:生成 Builder 模式的代码。
示例:
复制
@Builder
public class User {
private String name;
private int age;
}
// 使用
User user = User.builder()
.name("John")
.age(30)
.build();
2.9 @Slf4j
作用:自动生成日志对象(基于 SLF4J)。
示例:
@Slf4j
public class UserService {
public void doSomething() {
log.info("This is a log message.");
}
}
2.10 @Value
作用:
- 生成不可变类(所有字段为 final),包含 @Getter、@ToString、@EqualsAndHashCode 和 @AllArgsConstructor。
示例:
@Value
public class User {
String name;
int age;
}
2.11 @NonNull
作用:
- 标记字段或参数不能为 null,自动生成空值检查。
示例:
public class User {
@NonNull
private String name;
}
2.12 @Cleanup
作用:
- 自动关闭资源(如 InputStream、OutputStream)。
示例:
public void copyFile(String src, String dest) throws IOException {
@Cleanup InputStream in = new FileInputStream(src);
@Cleanup OutputStream out = new FileOutputStream(dest);
byte[] buffer = new byte[1024];
int length;
while ((length = in.read(buffer)) != -1) {
out.write(buffer, 0, length);
}
}
3. 高级用法
3.1 自定义 Builder
作用:
- 自定义 @Builder 的行为。
示例:
复制
@Builder(builderClassName = "UserBuilder", buildMethodName = "create")
public class User {
private String name;
private int age;
}
// 使用
User user = User.builder()
.name("John")
.age(30)
.create();
3.2 链式调用
作用:
- 通过 @Accessors(chain = true) 实现链式调用。
示例:
@Data
@Accessors(chain = true)
public class User {
private String name;
private int age;
}
// 使用
User user = new User().setName("John").setAge(30);
3.3 懒加载
作用:
- 通过 @Getter(lazy = true) 实现懒加载。
示例:
public class User {
@Getter(lazy = true)
private final double cachedValue = expensiveCalculation();
private double expensiveCalculation() {
return Math.random();
}
}
3.4 委托模式
Lombok 的 @Delegate 注解用于实现委托模式,它可以简化在一个类中委托另一个类的方法调用。通过使用 @Delegate,可以将一个对象的所有方法直接转发到另一个对象,从而避免重复代码。
作用:
- 当一个类需要使用另一个类的功能时,可以将这个类的实例作为一个字段,使用 @Delegate 自动转发方法调用。
- 通过委托,减少样板代码并提高代码的可读性。
import lombok.*;
@Data
class DelegateClass {
private String name;
public void printName() {
System.out.println("Name: " + name);
}
}
@Data
class MainClass {
@Delegate
private final DelegateClass delegate = new DelegateClass();
// 其他属性和方法
}
public class Main {
public static void main(String[] args) {
MainClass mainClass = new MainClass();
mainClass.setName("Alice"); // 委托设置名称
mainClass.printName(); // 委托打印名称
}
}
4. 注意事项
4.1 . Builder和NoArgsConstructor一起使用冲突问题
在使用 Lombok 的 @Builder
和 @NoArgsConstructor
注解时,可能会出现冲突问题。
比如如下报错:
Error:(17, 1) java: 无法将类 com.sayabc.groupclass.dtos.appoint.TeaPoolLogicalDelDto中的构造器 TeaPoolLogicalDelDto应用到给定类型;
这是因为 @Builder 生成的构造函数与 @NoArgsConstructor 生成的无参构造函数之间的潜在不兼容性。
4.1.1.问题描述
- @Builder:用于生成一个建造者模式的构造函数。
- @NoArgsConstructor:用于生成一个无参构造函数。
如果一个类同时使用这两个注解,那么在生成构造函数时可能会产生冲突,因为 @Builder 会生成一个带有参数的构造函数,而 @NoArgsConstructor 则生成无参构造函数。这样可能导致编译错误,尤其是当你希望类的所有字段都有默认值或强制要求某些字段必须在创建对象时被赋值。
4.1.2.解决方案
4.1.2.1.使用 @Builder 的 toBuilder 属性:
如果你希望能够使用无参构造函数,同时又想要支持建造者模式,可以为 @Builder 注解设置 toBuilder = true,这样可以在使用无参构造函数时仍然能够通过建造者模式创建对象。
import lombok.*;
@Data
@NoArgsConstructor
@Builder(toBuilder = true)
public class User {
private String name;
private int age;
}
4.1.2.2.自定义无参构造函数:
如果需要在无参构造函数中进行特定的初始化,可以手动定义一个无参构造函数,同时保留 @Builder 注解。
import lombok.*;
@Data
@Builder
public class User {
private String name;
private int age;
public User() {
// 初始化逻辑
this.name = "Default Name";
this.age = 0;
}
}
4.1.2.2.避免同时使用这两个注解:
考虑使用其中一个注解来满足需求。如果你需要建造者模式,可以去掉 @NoArgsConstructor;如果你只需要无参构造函数,可以去掉 @Builder。
4.1.3.示例代码
以下是一个示例,展示如何正确使用 @Builder 和 @NoArgsConstructor:
import lombok.*;
@Data
@NoArgsConstructor
@Builder(toBuilder = true)
public class User {
private String name;
private int age;
}
// 使用示例
public class Main {
public static void main(String[] args) {
User user = User.builder()
.name("Alice")
.age(25)
.build();
// 使用无参构造函数
User defaultUser = new User();
System.out.println(defaultUser);
}
}
在使用 Lombok 的 @Builder 和 @NoArgsConstructor 时,需要注意它们之间的潜在冲突。可以通过设置 @Builder 的 toBuilder 属性、手动定义无参构造函数,或选择性使用其中一个注解来解决这些问题。根据具体需求合理设计类的构造函数和建造者模式,可以避免编译错误和逻辑不一致。
4.2.其他问题
IDE 支持:
- 确保 IDE 安装了 Lombok 插件,否则代码可能无法编译或报错。
与其他库的兼容性:
- 某些库(如 JPA)可能需要显式的 getter/setter 方法,Lombok 的注解可能不适用。
代码可读性:
- 过度使用 Lombok 可能导致代码可读性下降,建议根据团队规范合理使用。
版本兼容性:-
- 确保 Lombok 版本与 JDK 和其他依赖库兼容。
5.自定义注解原理
5.1. 注解处理器
Lombok 使用 Java 的注解处理机制,这是一种在编译时处理注解的机制。注解处理器能够在编译期间
分析代码,并根据注解的使用生成额外的代码。
5.1.1 javax.annotation.processing 包
Lombok 实现了 javax.annotation.processing.Processor 类
,并通过 Java 的注解处理器 API (JSR 269: Pluggable Annotation Processing API
)来处理自定义注解。当编译器在编译过程中遇到 Lombok 的注解时,它会调用相应的处理器。
5.2. 编译时代码生成
Lombok 在编译时生成相应的 Java 代码。这些生成的代码会被编译器直接编译成字节码,从而在程序运行时可用。
5.2.1 AST(抽象语法树)
Lombok 通过解析源代码生成 AST,然后根据注解的信息修改 AST,添加所需的方法和构造函数。例如,当你使用 @Getter 注解时,Lombok 会在 AST 中添加对应的 getter 方法。
5.2.2 代码生成
在 AST 修改完成后,Lombok 会生成 Java 源代码并将其写入临时文件,这些文件在后续的编译过程中会被编译器处理。
5.3. 依赖于 IDE 插件
为了使 IDE 能够正确识别生成的代码,Lombok 提供了专门的插件。这些插件能够在代码编辑器中提供代码补全、重构等功能,确保开发者在使用 Lombok 时不会受到不便。
5.3.1 IntelliJ IDEA 和 Eclipse
在 IntelliJ IDEA 和 Eclipse 等主流 IDE 中,用户需要安装 Lombok 插件,以便 IDE 理解和展示 Lombok 生成的代码。
5.4. 运行时无影响
Lombok 的代码生成完全发生在编译时,因此在运行时不会引入额外的性能开销。生成的代码与手动编写的代码在运行时表现一致。