-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Expand file tree
/
Copy pathlombok.md
More file actions
427 lines (329 loc) · 12.1 KB
/
lombok.md
File metadata and controls
427 lines (329 loc) · 12.1 KB
1
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
---
category:
- Java企业级开发
tag:
- Spring Boot
title: Spring Boot 整合 Lombok,用注解简化 Java 代码,比如说 getter和setter
subTitle: 整合 Lombok
discription: Lombok 是一个流行的 Java 库,旨在通过注解的方式简化 Java 代码,减少模板代码的编写。它通过在编译时自动生成常用的 Java 代码(如 getters、setters、constructors、toString、equals、hashCode 等方法),使得源代码更加简洁。
---
Spring Boot 早在 2.1.x 版本后就在 starter 中内置了 Lombok 依赖,Intellij IDEA 也早在 IDEA 2020.3 版本的时候内置了 Lombok 插件。为什么它们都要支持 Lombok 呢?Lombok 到底有啥牛皮的?今天我们就来补上这一课。
## Lombok 的自我介绍
`Lombok` 在官网是这样作自我介绍的:
> Project Lombok makes java a spicier language by adding 'handlers' that know how to build and compile simple, boilerplate-free, not-quite-java code.
大致的意思就是:`Lombok` 是个好类库,可以为 Java 代码添加一些“处理程序”,让其变得更简洁、更优雅。在我看来,`Lombok` 最大的好处就在于通过注解的形式来简化 Java 代码,简化到什么程度呢?
作为一名 Java 程序员,我相信你一定写过不少的 `getter / setter`,尽管可以借助 IDE 来自动生成,可一旦 `Javabean` 的属性很多,就免不了要产生大量的 `getter / setter`,这会让代码看起来不够简练,就像老太婆的裹脚布一样,又臭又长。
```java
class Cmower {
private int age;
private String name;
private BigDecimal money;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public BigDecimal getMoney() {
return money;
}
public void setMoney(BigDecimal money) {
this.money = money;
}
}
```
`Lombok` 可以通过注解的方式,在编译的时候自动为 `Javabean` 的属性生成 `getter / setter`,不仅如此,还可以生成构造方法、`equals`方法、`hashCode`方法,以及 `toString`方法。注意是在编译的时候哦,源码当中是没有 `getter / setter` 等等的。
```java
@Getter
@Setter
class CmowerLombok {
private int age;
private String name;
private BigDecimal money;
}
```
瞧一瞧,源码看起来苗条多了,对不对?
## 集成 Lombok
如果项目使用 `Maven` 构建的话,添加`Lombok` 的依赖就变得轻而易举了。
```xml
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.6</version>
<scope>provided</scope>
</dependency>
```
其中 `scope=provided`,就说明 `Lombok` 只在编译阶段生效。也就是说,`Lombok` 会在编译期静悄悄地将带 `Lombok` 注解的源码文件正确编译为完整的 `class` 文件。
SpringBoot 2.1.x 版本后不需要再显式地添加 Lombok 依赖了。之后,还需要为 Intellij IDEA 安装 `Lombok` 插件,否则 `Javabean` 的 `getter / setter` 就无法自动编译,也就不能被调用。不过,新版的 Intellij IDEA 也已经内置好了,不需要再安装。

## 常用的 Lombok 注解
### 1)@Getter / @Setter
`@Getter / @Setter` 用起来很灵活,比如说像下面这样:
```java
class CmowerLombok {
@Getter @Setter private int age;
@Getter private String name;
@Setter private BigDecimal money;
}
```
字节码文件反编译后的内容是:
```java
class CmowerLombok {
private int age;
private String name;
private BigDecimal money;
public int getAge() {
return this.age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return this.name;
}
public void setMoney(BigDecimal money) {
this.money = money;
}
}
```
### 2)@ToString
`@ToString` 注解可以生成 `toString` 方法,是打印日志的好帮手哦。
```java
@ToString
class CmowerLombok {
private int age;
private String name;
private BigDecimal money;
}
```
字节码文件反编译后的内容是:
```java
class CmowerLombok {
private int age;
private String name;
private BigDecimal money;
public String toString() {
return "CmowerLombok(age=" + this.age + ", name=" + this.name + ", money=" + this.money + ")";
}
}
```
### 3)@Val
在编写 JavaScript 代码时,我一直觉得 `var` 这个变量声明类型用起来特别方便。`Lombok` 也提供了一个类似的。
```java
class CmowerLombok {
public void test() {
val names = new ArrayList<String>();
names.add("沉默王二");
val name = names.get(0);
System.out.println(name);
}
}
```
字节码文件反编译后的内容是:
```java
class CmowerLombok {
public void test() {
ArrayList<String> names = new ArrayList();
names.add("沉默王二");
String name = (String) names.get(0);
System.out.println(name);
}
}
```
### 4)@Data
`@Data` 注解可以生成 `getter / setter`、`equals`、`hashCode`,以及 `toString`,是个总和的选项。
```java
@Data
class CmowerLombok {
private int age;
private String name;
private BigDecimal money;
}
```
字节码文件反编译后的内容是:
```java
class CmowerLombok {
private int age;
private String name;
private BigDecimal money;
public int getAge() {
return this.age;
}
public String getName() {
return this.name;
}
public BigDecimal getMoney() {
return this.money;
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public void setMoney(BigDecimal money) {
this.money = money;
}
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (!(o instanceof CmowerLombok)) {
return false;
} else {
CmowerLombok other = (CmowerLombok) o;
if (!other.canEqual(this)) {
return false;
} else if (this.getAge() != other.getAge()) {
return false;
} else {
Object this$name = this.getName();
Object other$name = other.getName();
if (this$name == null) {
if (other$name != null) {
return false;
}
} else if (!this$name.equals(other$name)) {
return false;
}
Object this$money = this.getMoney();
Object other$money = other.getMoney();
if (this$money == null) {
if (other$money != null) {
return false;
}
} else if (!this$money.equals(other$money)) {
return false;
}
return true;
}
}
}
protected boolean canEqual(Object other) {
return other instanceof CmowerLombok;
}
public int hashCode() {
int PRIME = true;
int result = 1;
int result = result * 59 + this.getAge();
Object $name = this.getName();
result = result * 59 + ($name == null ? 43 : $name.hashCode());
Object $money = this.getMoney();
result = result * 59 + ($money == null ? 43 : $money.hashCode());
return result;
}
public String toString() {
return "CmowerLombok(age=" + this.getAge() + ", name=" + this.getName() + ", money=" + this.getMoney() + ")";
}
}
```
### 5)@Slf4j
`@Slf4j` 可以用来生成注解对象,你可以根据自己的日志实现方式来选用不同的注解,比如说:`@Log`、`@Log4j`、`@Log4j2`、`@Slf4j` 等。
```java
@Slf4j
public class Log4jDemo {
public static void main(String[] args) {
log.info("level:{}","info");
log.warn("level:{}","warn");
log.error("level:{}", "error");
}
}
```
字节码文件反编译后的内容是:
```java
public class Log4jDemo {
private static final Logger log = LoggerFactory.getLogger(Log4jDemo.class);
public Log4jDemo() {
}
public static void main(String[] args) {
log.info("level:{}", "info");
log.warn("level:{}", "warn");
log.error("level:{}", "error");
}
}
```
### 6)@Builder
`@Builder` 注解可以用来通过建造者模式来创建对象,这样就可以通过链式调用的方式进行对象赋值,非常的方便。
```java
@Builder
@ToString
public class BuilderDemo {
private Long id;
private String name;
private Integer age;
public static void main(String[] args) {
BuilderDemo demo = BuilderDemo.builder().age(18).name("沉默王二").build();
System.out.println(demo);
}
}
```
字节码文件反编译后的内容是:
```java
public class BuilderDemo {
private Long id;
private String name;
private Integer age;
public static void main(String[] args) {
BuilderDemo demo = builder().age(18).name("沉默王二").build();
System.out.println(demo);
}
BuilderDemo(final Long id, final String name, final Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
public static BuilderDemo.BuilderDemoBuilder builder() {
return new BuilderDemo.BuilderDemoBuilder();
}
public String toString() {
return "BuilderDemo(id=" + this.id + ", name=" + this.name + ", age=" + this.age + ")";
}
public static class BuilderDemoBuilder {
private Long id;
private String name;
private Integer age;
BuilderDemoBuilder() {
}
public BuilderDemo.BuilderDemoBuilder id(final Long id) {
this.id = id;
return this;
}
public BuilderDemo.BuilderDemoBuilder name(final String name) {
this.name = name;
return this;
}
public BuilderDemo.BuilderDemoBuilder age(final Integer age) {
this.age = age;
return this;
}
public BuilderDemo build() {
return new BuilderDemo(this.id, this.name, this.age);
}
public String toString() {
return "BuilderDemo.BuilderDemoBuilder(id=" + this.id + ", name=" + this.name + ", age=" + this.age + ")";
}
}
}
```
除了我上面提到的这些,Lombok 还提供了同步注解 @Synchronized、自动抛出异常注解 @SneakyThrows、不可变对象 @Value、自动生成 hashCode 和 equals 方法的注解 [@EqualsAndHashCode ](/EqualsAndHashCode ) 等等,大家可以去尝试一下,顺带看一下反编译后的字节码,体验一下 Lombok 的工作原理。
## Lombok 的处理流程
一图胜千言,直接上图。

- javac 对源代码进行分析,生成一棵抽象语法树(AST)
- javac 编译过程中调用实现了JSR 269 的 Lombok 程序
- Lombok 对 AST 进行处理,找到 Lombok 注解所在类对应的语法树(AST),然后修改该语法树,增加 Lombok 注解定义的相应树节点(所谓代码)
- javac 使用修改后的抽象语法树生成字节码文件
Lombok 用起来虽然爽,但需要团队内部达成一致,就是要用大家都用,否则有些用了有些没用就会乱成一锅粥,很影响代码的整体风格。另外,假如有团队成员还在用 Eclipse,那么也得要求他安装 Lombok 插件,否则打开一个使用 Lombok 注解的项目就会无法通过编译。
如果一类使用了 Lombok 注解,通过类结构是可以查看到对应的方法的,比如说下图中的 toString 和 builder 方法。

打开 target 目录下的 .class 文件,就可以看到 Lombok 生成的反编译后的字节码文件,也可以验证 Lombok 是在编译阶段实现 Java 代码增强功能的。

## 小结
`Lombok` 是一个流行的 Java 库,旨在通过注解的方式简化 Java 代码,减少模板代码的编写。它通过在编译时自动生成常用的 Java 代码(如 getters、setters、constructors、toString、equals、hashCode 等方法),使得源代码更加简洁。
几乎所有正式项目都已经使用了 lombok,因为的确能够提升开发效率,减少代码量,提高代码可读性。但是,如果你的团队成员有人不会使用 lombok,那么你就需要考虑一下是否要使用 lombok 了,因为如果有人不会使用,那么他就无法阅读 lombok 生成的代码,这样就会影响团队的开发效率。
嗯,另外一个好的办法就是,你把这篇内容转发给他,让他好好学习一下,哈哈哈。