Lombok插件
# 1、Lombok介绍
- Lombok是一个Java库,它通过注解的方式简化了Java代码,提高了开发效率。
- 使用Lombok,开发人员可以不必手动编写诸如构造器、getter、setter、equals、hashCode和toString等方法,因为Lombok会自动生成这些方法。
- 此外,Lombok还提供了其他一些有用的注解,如@NonNull、@Getter、@Setter和@ToString等。这些注解可以用于类、字段和参数,以简化代码并减少错误。
- 使用Lombok可以使代码更加简洁、易于阅读和维护。
简单案例
- 未使用Lombok时,我们需要手动编写getter和setter方法
public class StudentPO {
private Long id;
private String name;
private Integer age;
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 Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
- 使用Lombok后,我们只需要添加相应的注解
@Data
public class StudentPO {
private Long id;
private String name;
private Integer age;
}
2
3
4
5
6
7
8
9
10
# 2. 配置Lombok
# 2.1、Idea安装Lombok插件
Idea这里我已经安装了Lombok插件,注意看这里其实可以看到Lombok提供了哪些注解可以供我们使用。
Lombok注解需要配合这个Lombok插件使用,不是说我们的类不用getter和setter方法了,而是在编译阶段,Lombok插件会自动帮我们生成这些方法。
# 2.2、引入Lombok依赖
在项目中引入Lombok依赖,所有版本可以在Maven中央仓库中找到。Project Lombok依赖 (opens new window)
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
2
3
4
5
6
# 3、Lombok注解
# 3.1、val、var
val
是用于声明局部变量的,它会根据初始化的表达式来对变量类型进行推断。使用val的局部变量会被声明为final。它可以使用与foreach的循环中,val不可以用于成员变量,并且它的初始化表达式是必须的(即必须在声明的时候给出初始化的表达式)。
public static void main(String[] args) {
val sets = new HashSet<String>();
val lists = new ArrayList<String>();
val maps = new HashMap<String, String>();
//=>相当于如下
final Set<String> sets2 = new HashSet<>();
final List<String> lists2 = new ArrayList<>();
final Map<String, String> maps2 = new HashMap<>();
}
2
3
4
5
6
7
8
9
var
跟val
相似,只不过var
它标记的局部变量并不是final的。
# 3.2、@Data
注解在类上,相当于同时使用了@ToString、@EqualsAndHashCod- e、@Getter、@Setter 和@RequiredArgsConstrutor 这些注解。
@Data
public class StudentPO {
private Long id;
private String name;
private Integer age;
}
2
3
4
5
6
7
8
9
10
# 3.3、@EqualsAndHashCode
在类上使用,自动生成 equals 和 hashCode 方法。
/**
* exclude:排除的字段
* callSuper:是否调用超类(superclass)的 toString() 方法
*/
@EqualsAndHashCode(exclude = {"id", "shape"}, callSuper = false)
public class LombokDemo {
private int id;
private String shap;
}
2
3
4
5
6
7
8
9
# 3.4、@Getter/@Setter
- @Getter:在 JavaBean 或类 JavaBean 中使用,使用此注解会生成对应的 getter 方法。
- @Setter:在 JavaBean 或类 JavaBean 中使用,使用此注解会生成对应的 setter 方法。
@Setter(AccessLevel.PUBLIC)
@Getter(AccessLevel.PROTECTED)
private int id;
private String shap;
2
3
4
# 3.5、@Getter(lazy=true)
当你在一个字段上使用 @Getter(lazy=true) 注解时,Lombok 会生成一个延迟加载的 getter 方法。这意味着该方法不会立即在对象创建时加载字段的值,而是在第一次调用该方法时才加载。这样可以减少内存占用,特别是对于大型对象或从数据库中加载的数据。
public class Example {
@Getter(lazy=true)
private final String name = loadNameFromDatabase();
private String loadNameFromDatabase() {
// 从数据库中加载名字的逻辑
return "John Doe";
}
}
2
3
4
5
6
7
8
9
# 3.6、@NoArgsConstructor
在 JavaBean 或类 JavaBean 中使用,使用此注解会生成对应的无参构造方法。
@NoArgsConstructor
@RequiredArgsConstructor(staticName = "of")
@AllArgsConstructor
public class LombokDemo {
@NonNull
private int id;
@NonNull
private String shap;
private int age;
public static void main(String[] args) {
new LombokDemo(1, "circle");
//使用静态工厂方法
LombokDemo.of(2, "circle");
//无参构造
new LombokDemo();
//包含所有参数
new LombokDemo(1, "circle", 2);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 3.7、AllArgsConstructor
在 JavaBean 或类 JavaBean 中使用,使用此注解会生成对应的全参构造方法。
@AllArgsConstructor
public class LombokDemo {
@NonNull
private int id;
@NonNull
private String shap;
private int age;
public static void main(String[] args) {
//包含所有参数
new LombokDemo(1, "circle", 2);
}
}
2
3
4
5
6
7
8
9
10
11
12
# RequiredArgsConstructor
在 JavaBean 或类 JavaBean 中使用,把所有@NonNull属性和final属性作为参数的构造函数,如果指定 staticName = “of”参数,同时还会生成一个返回类对象的静态工厂方法。
// staticName = "of"表示生成的构造函数名称将为of`。
@RequiredArgsConstructor(staticName = "of")
public class Person {
private final String name;
private String address;
// 相当于
public static void of(String name) {
this.name = name;
}
}
2
3
4
5
6
7
8
9
10
11
Person 类有两个字段:name 和 address。由于 name 字段是 final 的,Lombok 将自动生成一个包含 name 参数的构造函数。而 address 字段是非 final 的,因此不会自动添加到构造函数中。
# 3.9、@Value
用在类上,是@Data 的不可变形式,相当于为属性添加 final 声明,只提供 getter 方法,而不提供 setter 方法。
@Value
public class LombokDemo {
@NonNull
private int id;
@NonNull
private String shap;
private int age;
//相当于
private final int id;
public int getId() {
return this.id;
}
// ...
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# 3.10、@NonNull
用在方法参数前,会自动对该参数进行非空校验,为空抛出 NullPointerException。
public void notNullExample(@NonNull String string) {
string.length();
}
//=>相当于
public void notNullExample(String string) {
if (string != null) {
string.length();
} else {
throw new NullPointerException("null");
}
}
2
3
4
5
6
7
8
9
10
11
# 3.11、@Builder
用在类、构造器、方法上,为你提供复杂的 builder APIs。 让你可以像如下方式一样调用 Person.builder().name("Adam Savage").city("San Francisco").job("Mythbusters").job("Unchained Reaction").build()
@Builder
public class BuilderExample {
private String name;
private int age;
@Singular
private Set<String> occupations;
public static void main(String[] args) {
BuilderExample test = BuilderExample.builder().age(11).name("test").build();
}
}
2
3
4
5
6
7
8
9
10
# 3.12、@Singular
用于集合字段。当你在一个集合字段上使用 @Singular 注解时,Lombok 会生成一个添加元素的方法,该方法接受一个参数,并将该参数添加到集合中。
public class Person {
@Singular
private List<String> hobbies = new ArrayList<>();
}
2
3
4
hobbies 是一个集合字段,使用了 @Singular 注解。这意味着 Lombok 将自动生成一个名为 addHobby 的方法,该方法接受一个字符串参数,并将该参数添加到 hobbies 集合中。
# 3.13、@SneakyThrows
自动抛受检异常,而无需显式在方法上使用 throws 语句。
public class Test {
@SneakyThrows()
public void read() {
InputStream inputStream = new FileInputStream("");
}
@SneakyThrows
public void write() {
throw new UnsupportedEncodingException();
}
//相当于
public void read() throws FileNotFoundException {
InputStream inputStream = new FileInputStream("");
}
public void write() throws UnsupportedEncodingException {
throw new UnsupportedEncodingException();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 3.14、@Synchronized
用在方法上,将方法声明为同步的,并自动加锁。
public class SynchronizedDemo {
@Synchronized
public static void hello() {
System.out.println("world");
}
//相当于
private static final Object $LOCK = new Object[0];
public static void hello() {
synchronized ($LOCK) {
System.out.println("world");
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
# 3.15、@Log
根据不同的注解生成不同类型的 log 对象,但是实例名称都是 log,有六种可选实现类
- @CommonsLog:Creates log = org.apache.commons.logging.LogFactory.getLog(LogExample.class);
- @Log:Creates log = java.util.logging.Logger.getLogger(LogExample.class.getName());
- @Log4j:Creates log = org.apache.log4j.Logger.getLogger(LogExample.class);
- @Log4j2:Creates log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);
- @Slf4j:Creates log = org.slf4j.LoggerFactory.getLogger(LogExample.class);
- @XSlf4j:Creates log = org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);
import lombok.Log;
@Log
public class MyClass {
public void doSomething() {
// 执行一些操作
}
}
// @Log 注解告诉 Lombok 在 MyClass 类上自动生成日志记录代码。
// 具体的日志记录代码取决于你使用的日志框架。如果你使用的是 SLF4J 日志框架,Lombok 将自动生成类似于以下的代码:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MyClass {
private static final Logger log = LoggerFactory.getLogger(MyClass.class);
public void doSomething() {
log.info("Doing something");
// 执行一些操作
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 3.16、@Cleanup
自动管理资源,用在局部变量之前,在当前变量范围内即将执行完毕退出前会清理资源,生成 try-finally 的代码关闭流。
public static void main(String[] args) {
try {
@Cleanup
InputStream inputStream = new FileInputStream(args[0]);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
//=>相当于
InputStream inputStream = null;
try {
inputStream = new FileInputStream(args[0]);
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 3.17、@ToString
在 JavaBean 或类 JavaBean 中使用,使用此注解会自动复写 toString 方法。
/**
* exclude:排除的字段
* callSuper:是否调用超类(superclass)的 toString() 方法
* includeFieldNames:是否包含字段名称
*/
@ToString(exclude = "id", callSuper = true, includeFieldNames = true)
public class LombokDemo {
private int id;
private String name;
private int age;
public static void main(String[] args) {
//输出LombokDemo(super=LombokDemo@48524010, name=null, age=0)
System.out.println(new LombokDemo());
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 3.18、@With
作用于类,生成多个 with + 变量名的方法(个数为所有成员变量,不包含 @NonNull),作用于变量,生成 with + 变量名的方法 返回当前对象,需要提供全参(不包含静态变量)构造方法
@AllArgsConstructor
@With
public class Test {
private final String name;
private Integer age;
}
// 相当于
public class Test {
private final String name;
private Integer age;
public Test(String name, Integer age) {
this.name = name;
this.age = age;
}
public Test withName(String name) {
return new Test(name, this.age);
}
public Test withAge(Integer age) {
return new Test(this.name, age);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 4、Experimental注解
在 lombok.experimental
包下,lombok除了已经推荐使用的基本功能,还维护了一个创新型的注解,有些功能有违常规对java认知,或者只支持eclipse,其他IDE支持有问题,甚至某些环境完全不可用。因此没有正式使用。但是的确很有创意,这些注解已经在jar中提供,只不过它是归在lombok.experimental.
包中;而基本功能在lombok.
包中。
# 4.1、@Accessors
默认情况下,没什么作用,需要设置参数
- chain:为true时,setter链式返回,即setter的返回值为this
- fluent:为true时,默认设置chain为true,setter的方法名修改为字段名
- prefix:set方法忽略指定的前缀。
@Data
@Accessors(chain=true)
public class User {
private Integer id;
private String name;
private Integer age;
public static void main(String[] args) {
//开起chain=true后可以使用链式的set
User user=new User().setAge(31).setName("pollyduan");//返回对象
System.out.println(user);
}
}
@Data
@Accessors(fluent=true)
public class User {
private Integer id;
private String name;
private Integer age;
public static void main(String[] args) {
//fluent=true开启后默认chain=true,故这里也可以使用链式set
User user=new User().age(31).name("pollyduan");//不需要写set
System.out.println(user);
}
}
@Data
@Accessors(prefix = "f")
public class User {
private String fName = "Hello, World!";
public static void main(String[] args) {
User user=new User();
user.setName("pollyduan");//注意方法名
System.out.println(user);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# 4.2、@Delegate
代理模式,把字段的方法代理给类,默认代理所有方法
- types:指定代理的方法
- excludes:和types相反
public class Example {
private interface Add {
boolean add(String x);
boolean addAll(Collection<? extends String> x);
}
private @Delegate(types = Add.class) List<String> strings;
}
// 生成结果
public class Example {
private List<String> strings;
public Example() {
}
public boolean add(String x) {
return this.strings.add(x);
}
public boolean addAll(Collection<? extends String> x) {
return this.strings.addAll(x);
}
private interface Add {
boolean add(String var1);
boolean addAll(Collection<? extends String> var1);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 4.3、@ExtensionMethod
拓展方法,向现有类型“添加”方法,而无需创建新的派生类型。有点像kotlin的扩展函数。
@ExtensionMethod({Arrays.class, Extensions.class})
public class Example {
public static void main(String[] args) {
int[] intArray = {5, 3, 8, 2};
intArray.sort();
int num = 1;
num = num.increase();
Arrays.stream(intArray).forEach(System.out::println);
System.out.println("num = " + num);
}
}
class Extensions {
public static int increase(int num) {
return ++num;
}
}
// 生成如下
public class Example {
public Example() {
}
public static void main(String[] args) {
int[] intArray = new int[]{5, 3, 8, 2};
Arrays.sort(intArray);
int num = 1;
int num = Extensions.increase(num);
IntStream var10000 = Arrays.stream(intArray);
PrintStream var10001 = System.out;
System.out.getClass();
var10000.forEach(var10001::println);
System.out.println("num = " + num);
}
}
/**
* 输出结果:
* 2
* 3
* 5
* 8
* num = 2
*/
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# 4.4、@FieldDefaults
定义类、字段的修饰符
- AccessLevel:访问权限修饰符
- makeFinal:是否加final
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
public class Gulnazar {
String name = "xygalaxy";
}
// ==>相当于
public class Gulnazar {
private final String name = "xygalaxy";
}
2
3
4
5
6
7
8
9
# 4.5、@FieldNameConstants
为你的字段生成一个以字段名称为值的常量
- prefix:前缀
- suffix:后缀
@FieldNameConstants
public class FieldNameConstantsExample {
private final String iAmAField;
private final int andSoAmI;
@FieldNameConstants.Exclude
private final int asAmI;
}
//==>相当于
public class FieldNameConstantsExample {
private final String iAmAField;
private final int andSoAmI;
private final int asAmI;
public static final class Fields {
public static final String iAmAField = "iAmAField";
public static final String andSoAmI = "andSoAmI";
}
}
// 最终其实也就是相当于
public class FieldNameConstantsExample {
private static final String iAmAField = "iAmAField";
private static final int andSoAmI = "andSoAmI";
private final int asAmI;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class Example {
@FieldNameConstants(prefix = "PREFIX_", suffix = "_SUFFIX")
private String foo;
}
public class Example {
public static final String PREFIX_FOO_SUFFIX = "foo";
private String foo;
public Example() {
}
}
2
3
4
5
6
7
8
9
10
11
12
# 4.6、@Helper
让你可以在方法内部中写方法
public class HelperExample {
int someMethod(int arg1) {
int localVar = 5;
@Helper class Helpers {
int helperMethod(int arg) {
return arg + localVar;
}
}
return helperMethod(10);
}
}
// 相当于
public class HelperExample {
int someMethod(int arg1) {
int localVar = 5;
class Helpers {
int helperMethod(int arg) {
return arg + localVar;
}
}
Helpers $Helpers = new Helpers();
return $Helpers.helperMethod(10);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 4.7、@NonFinal
设置不为Final,@FieldDefaults和@Value也有这功能
public class ZhuXuDan {
@NonFinal String age;
@NonFinal final String name = "xygalaxy"; // 必须初始化
}
2
3
4
5
# 4.8、@PackagePrivate
作用于类和变量,相当于访问修饰符的 default,没什么用
# 4.9、@SuperBuilder
支持对于基类成员变量赋值,算是 @Builder 的升级版
# 4.10、@Tolerate
实现对冲突的兼容,作用于方法上,没什么大用,可以配合 @Builder 使用
# 4.11、@UtilityClass
作用于类,将类标记为 final,并且类、内部类中的方法、字段都标记为 static
# 5、Lombok原理解析
# 5.1、JSR-269提案
在JDK 5之后,Java语言提供了对注解(Annotations)的支持。注解原本是设计为在程序运行期间发挥作用的。然而,在JDK 6中,通过JSR-269提案,插入式注解处理器(Pluggable Annotation Processors)的标准API被引入,使得注解处理器可以在编译期对代码中的特定注解进行处理,影响前端编译器的工作过程。
# 5.2、Lombok编译过程
- 当编译器在编译Java代码时,Lombok的注解处理器会解析Lombok的注解,并在编译期间自动生成对应的方法代码。
- 例如,当使用@Data注解时,相当于同时使用了@Getter、@Setter、@EqualsAndHashCode、@ToString等注解。编译器会在编译期根据类的成员变量自动生成对应的getter/setter、equals/hashCode、toString等方法。
Lombok编译过程
- javac对源代码进行分析,生成了一棵抽象语法树(AST)
- 运行过程中调用实现了“JSR 269 API”的Lombok程序
- 此时Lombok就对第一步骤得到的AST进行处理,找到@Data注解所在类对应的语法树(AST),然后修改该语法树(AST),增加getter和setter方法定义的相应树节点
- javac使用修改后的抽象语法树(AST)生成字节码文件,即给class增加新的节点(代码块)