Lombok 快速入門

Lombok介紹:
Lombok其實(shí)就是取名自龍目島(Pulau Lombok),龍目島是印度尼西亞西努沙登加拉(Nusa Tenggara Barat)省島嶼,西隔龍目海峽面對(duì)巴厘島,東隔阿拉斯(Alas)海峽面松巴哇(Sumbawa)島,北瀕爪哇海,南臨印度洋。
在編程上,Lombok是一個(gè)可以通過簡單的注解形式來幫助我們簡化消除一些必須有但顯得很臃腫的Java代碼的工具,通過使用對(duì)應(yīng)的注解,可以在編譯源碼的時(shí)候生成對(duì)應(yīng)的方法。簡而言之,一句話就是:通過簡單的注解來精簡代碼達(dá)到消除冗長代碼的目的。
Lombok官網(wǎng):https://projectlombok.org/
github地址:https://github.com/rzwitserloot/lombok
Lombok優(yōu)點(diǎn):
提高編碼效率
使代碼更簡潔
消除冗長代碼
避免修改字段名字時(shí)忘記修改方法名
注:IDE上必須要支持Lombok,否則IDE會(huì)報(bào)錯(cuò)。
為什么說Lombok可以使代碼更簡潔、可以消除冗長代碼呢?我們來拿lombok官網(wǎng)的一個(gè)例子來說:
public class Mountain{
private String name;
private double longitude;
private String country;
}要使用這個(gè)對(duì)象,必須還要寫一些getter和setter方法,可能還要寫一個(gè)構(gòu)造器、equals方法、或者h(yuǎn)ash方法。這些方法很冗長而且沒有技術(shù)含量,我們叫它樣板式代碼。
lombok的主要作用是通過一些注解,消除樣板式代碼,像這樣:
@Data
public class Mountain{
private String name;
private double longitude;
private String country;
}然后可以看到這個(gè)類自動(dòng)生成了這些方法:

如果覺得@Data這個(gè)注解有點(diǎn)簡單粗暴的話,Lombok提供一些更精細(xì)的注解,比如@Getter、@Setter,(這兩個(gè)是field注解),@ToString,@AllArgsConstructor(這兩個(gè)是類注解)。這些可能是最常見的用法,更詳細(xì)的用法可以參考[Lombok feature]overview(https://projectlombok.org/features/)
Lombok既是一個(gè)IDE插件,也是一個(gè)項(xiàng)目要依賴的jar包。Lombok是依賴jar包的原因是因?yàn)榫幾g時(shí)要用它的注解。是插件的原因是他要在編譯器編譯時(shí)通過操作AST(抽象語法樹)改變字節(jié)碼生成。也就是說他可以改變java語法.。他不像spring的依賴注入或者h(yuǎn)ibernate的orm一樣是運(yùn)行時(shí)的特性,而是編譯時(shí)的特性。
Lombok原理:
Lombok 實(shí)現(xiàn)了 JSR 269 Pluggable Annotation Processing API 規(guī)范,也就是可插拔注釋處理
javac 從 JDK6 開始支持 “JSR 269 API” 規(guī)范
只要程序?qū)崿F(xiàn)了該API,就能在javac運(yùn)行的時(shí)候得到調(diào)用
而Lombok實(shí)現(xiàn)了 “JSR 269 API” 規(guī)范 ,在編譯時(shí),javac編譯源碼的具體流程如下:

1.javac對(duì)源代碼(Source File)進(jìn)行分析(Parse),生成一棵抽象語法樹(AST)
2.運(yùn)行過程(Annotation Processing)中調(diào)用實(shí)現(xiàn)了 "JSR 269 API" 的Lombok程序(Lombok Annotation Processor)
3.此時(shí)Lombok就對(duì)第一步驟得到的AST進(jìn)行處理(Lombok Annotation Handler),找到@Data注解所在類對(duì)應(yīng)的語法樹(AST),然后修改該語法樹(AST),增加getter和setter方法定義的相應(yīng)樹節(jié)點(diǎn)
4.javac使用修改后的抽象語法樹(Modified AST)進(jìn)行分析生成(Analyze and Generate)字節(jié)碼文件(Byte Code)
添加Lombok到項(xiàng)目中
創(chuàng)建一個(gè)Maven項(xiàng)目,通過pom.xml配置Lombok依賴到項(xiàng)目中,配置依賴如下:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
</dependency>IDEA安裝Lombok插件
然后還需要在IDE中安裝Lombok插件,我這里使用的是IDEA,所以先以IDEA為例做演示。點(diǎn)擊右上角的 File -> setting -> Plugins :

搜索Lombok Plugin進(jìn)行安裝:

安裝完成后,重啟IDEA:

Eclipse安裝Lombok插件:
1.安裝該插件時(shí)最好關(guān)閉Eclipse,然后在官網(wǎng)中下載lombok.jar,下載地址
2.將 lombok.jar 放在eclipse安裝目錄下,和 eclipse.ini 文件平級(jí)的。

3.雙擊運(yùn)行 lombok.jar
如果沒法直接雙擊運(yùn)行的話,就在 lombok.jar 的目錄下,打開cmd命令行,運(yùn)行如下命令:
java -jar lombok.jar如果以下提示的權(quán)限問題則使用管理員身份運(yùn)行即可:

注:Mac/Linux 系統(tǒng)下則使用 sudo java -jar lombok.jar 命令進(jìn)行運(yùn)行即可,但是要確保執(zhí)行用戶有sudo權(quán)限。
成功運(yùn)行后會(huì)彈框如下框,一開始可能會(huì)加載些東西,加載完成后界面如下:

安裝成功后如下圖:

打開Eclipse,看看是否已安裝Lombok插件,如下則是安裝成功:

Lombok注解
Lombok 常用的注解:
| 注解 | 描述 |
|---|---|
| @Getter / @Setter | 可以作用在類上和屬性上,放在類上,會(huì)對(duì)所有的非靜態(tài)(non-static)屬性生成Getter/Setter方法,放在屬性上,會(huì)對(duì)該屬性生成Getter/Setter方法。并可以使用該注解中的AccessLevel屬性來指定Getter/Setter方法的訪問級(jí)別。 |
| @ToString | 生成toString方法,默認(rèn)情況下,會(huì)輸出類名、所有屬性,屬性會(huì)按照順序輸出,以逗號(hào)分割。可以使用該注解中的exclude屬性來指定生成的toSpring方法不包含對(duì)象中的哪些字段,或者使用of屬性來指定生成的toSpring方法只包含對(duì)象中的哪些字段 |
| @EqualsAndHashCode | 默認(rèn)情況下,會(huì)使用所有非瞬態(tài)(non-transient)和非靜態(tài)(non-static)字段來生成equals和hascode方法,也可以使用exclude或of屬性。 |
| @NoArgsConstructor | 生成無參構(gòu)造器 |
| @RequiredArgsConstructor | 會(huì)生成一個(gè)包含標(biāo)識(shí)了@NonNull注解的變量的構(gòu)造方法。生成的構(gòu)造方法是private,如果想要對(duì)外提供使用的話,可以使用staticName選項(xiàng)生成一個(gè)static方法。 |
| @AllArgsConstructor | 生成全參構(gòu)造器,當(dāng)我們需要重載多個(gè)構(gòu)造器的時(shí)候,Lombok就無能為力了。 |
| @Slf4j | 該注解是用來解決不用每次都寫 private final Logger logger = LoggerFactory.getLogger(XXX.class); 這句代碼的。使用的日志框架是LogBack |
| @Log4j | 該注解也是用來解決不用每次都寫日志對(duì)象聲明語句的,從字面上也可以看出,使用的日志框架是log4j |
| @Data | 該注解是 @ToString、@EqualsAndHashCode注解,和所有屬性的@Getter注解, 以及所有non-final屬性的@Setter注解的組合,通常情況下,我們使用這個(gè)注解就足夠了。 |
以上只列出了部分常用注解,更多注解的使用方式,請(qǐng)參考 官網(wǎng)關(guān)于注解的文檔
反編譯大法
當(dāng)我們想查看.class文件的源碼時(shí),可以使用Java反編譯工具:
Java Decompiler
JD 官網(wǎng)地址
分為以下幾類
JD-GUI,獨(dú)立的圖形化軟件
JD-Eclipse,可以集成到Eclipse插件
JD-Intellij,可以集成到IDEA插件
這里提到反編譯工具的原因是因?yàn)長ombok是編譯時(shí)修改的抽象語法樹,所以我們想查看編譯后的.class文件的源碼就需要使用反編譯工具。這里所介紹到的 Java Decompiler 就是用來幫助我們?cè)谑褂肔ombok遇到問題時(shí),去驗(yàn)證編譯后的.class文件的。
使用Lombok時(shí)需要注意的點(diǎn)
在類需要序列化、反序列化時(shí)或者需要詳細(xì)控制字段時(shí),應(yīng)該謹(jǐn)慎考慮是否要使用Lombok,因?yàn)樵谶@種情況下容易出問題。例如:Jackson、Json 序列化
使用Lombok雖然能夠省去手動(dòng)創(chuàng)建setter和getter方法等繁瑣事情,但是卻降低了源代碼文件的可讀性和完整性,減低了閱讀源代碼的舒適度
使用@Slf4j還是@Log4j注解,需要根據(jù)實(shí)際項(xiàng)目中使用的日志框架來選擇。
Lombok并非處處適用,我們需要選擇適合的地方使用Lombok,例如pojo是一個(gè)好地方,因?yàn)閜ojo很單純
Lombok實(shí)戰(zhàn)
我這里拿之前項(xiàng)目中的一個(gè) Category 類來做為演示的例子,在使用Lombok之前,這個(gè)類里是寫了getter setter方法以及構(gòu)造函數(shù)的。現(xiàn)在我們使用Lombok將代碼改造如下:
package org.mmall.pojo;
import lombok.*;
import java.util.Date;
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(of = "id")
@ToString(exclude = "updateTime")
public class Category {
private Integer id;
private Integer parentId;
private String name;
private Boolean status;
private Integer sortOrder;
private Date createTime;
private Date updateTime;
}編譯后生成的代碼如下,使用反編譯工具進(jìn)行查看:
package org.mmall.pojo;
import java.beans.ConstructorProperties;
import java.util.Date;
public class Category {
private Integer id;
private Integer parentId;
private String name;
private Boolean status;
private Integer sortOrder;
private Date createTime;
private Date updateTime;
public Integer getId() {
return this.id;
}
public Integer getParentId() {
return this.parentId;
}
public String getName() {
return this.name;
}
public Boolean getStatus() {
return this.status;
}
public Integer getSortOrder() {
return this.sortOrder;
}
public Date getCreateTime() {
return this.createTime;
}
public Date getUpdateTime() {
return this.updateTime;
}
public void setId(Integer id) {
this.id = id;
}
public void setParentId(Integer parentId) {
this.parentId = parentId;
}
public void setName(String name) {
this.name = name;
}
public void setStatus(Boolean status) {
this.status = status;
}
public void setSortOrder(Integer sortOrder) {
this.sortOrder = sortOrder;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
public Category() {
}
@ConstructorProperties({"id", "parentId", "name", "status", "sortOrder", "createTime", "updateTime"})
public Category(Integer id, Integer parentId, String name, Boolean status, Integer sortOrder, Date createTime, Date updateTime) {
this.id = id;
this.parentId = parentId;
this.name = name;
this.status = status;
this.sortOrder = sortOrder;
this.createTime = createTime;
this.updateTime = updateTime;
}
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (!(o instanceof Category)) {
return false;
} else {
Category other = (Category)o;
if (!other.canEqual(this)) {
return false;
} else {
Object this$id = this.getId();
Object other$id = other.getId();
if (this$id == null) {
if (other$id != null) {
return false;
}
} else if (!this$id.equals(other$id)) {
return false;
}
return true;
}
}
}
protected boolean canEqual(Object other) {
return other instanceof Category;
}
public int hashCode() {
int PRIME = true;
int result = 1;
Object $id = this.getId();
int result = result * 59 + ($id == null ? 43 : $id.hashCode());
return result;
}
public String toString() {
return "Category(id=" + this.getId() + ", parentId=" + this.getParentId() + ", name=" + this.getName() + ", status=" + this.getStatus() + ", sortOrder=" + this.getSortOrder() + ", createTime=" + this.getCreateTime() + ")";
}
}如上,從反編譯后的代碼可以看到,getter setter方法和無參、全參構(gòu)造器以及equals、hashcode、toString方法都生成出來了。在@EqualsAndHashCode注解中我們使用of屬性指定只對(duì)比對(duì)象中id這個(gè)字段,所以生成的equals和hashcode只使用id這個(gè)字段作為因子,默認(rèn)不指定的情況下是使用對(duì)象中所有的字段作為因子。而在@ToString注解中,我們使用exclude屬性指定updateTime這字段不被輸出,所以Lombok生成的toString方法中沒有包含updateTime這個(gè)字段。
我們?cè)賮硌菔疽幌翤Getter、@Setter以及@RequiredArgsConstructor注解的使用,新建一個(gè)測(cè)試類,編輯代碼如下:
package org.mmall.pojo;
import lombok.Getter;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@RequiredArgsConstructor(staticName = "getInstance")
public class Test {
private String name;
@NonNull
private int age;
}編譯后生成的代碼如下,使用反編譯工具進(jìn)行查看:
package org.mmall.pojo;
import java.beans.ConstructorProperties;
import lombok.NonNull;
public class Test {
private String name;
@NonNull
private int age;
public String getName() {
return this.name;
}
@NonNull
public int getAge() {
return this.age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(@NonNull int age) {
this.age = age;
}
@ConstructorProperties({"age"})
private Test(@NonNull int age) {
this.age = age;
}
public static Test getInstance(@NonNull int age) {
return new Test(age);
}
}可以看到,@RequiredArgsConstructor注解會(huì)生成一個(gè)包含標(biāo)識(shí)了@NonNull注解的變量的構(gòu)造方法,并且生成的構(gòu)造方法是private的,使用staticName選項(xiàng)可以生成一個(gè)可以得到該對(duì)象實(shí)例的static方法。
接下來演示一下@Slf4j注解的使用,因?yàn)槲翼?xiàng)目中使用的是logback,所以使用@Slf4j注解,如果使用的是log4j,則使用@Log4j注解,兩者的使用方式是一樣的。代碼如下:
...
@Service("iCategoryService")
@Slf4j
public class CategoryServiceImpl implements ICategoryService {
public ServerResponse<List<Category>> getChildrenParallelCategory(Integer categoryId) {
List<Category> categoryList = categoryMapper.selectCategoryChildrenByParentId(categoryId);
if (CollectionUtils.isEmpty(categoryList)) {
log.info("未找到當(dāng)前分類的子分類");
}
return ServerResponse.createBySuccess(categoryList);
}
}編譯后生成的代碼如下,使用反編譯工具進(jìn)行查看:
...
@Service("iCategoryService")
public class CategoryServiceImpl implements ICategoryService {
private static final Logger log = LoggerFactory.getLogger(CategoryServiceImpl.class);
public ServerResponse<List<Category>> getChildrenParallelCategory(Integer categoryId) {
List<Category> categoryList = this.categoryMapper.selectCategoryChildrenByParentId(categoryId);
if (CollectionUtils.isEmpty(categoryList)) {
log.info("未找到當(dāng)前分類的子分類");
}
return ServerResponse.createBySuccess(categoryList);
}
}可以看到,Lombok會(huì)自動(dòng)幫我們生成log對(duì)象的聲明代碼,這樣我們就不需要總是每個(gè)類都去寫這句代碼了。
出處:https://blog.51cto.com/zero01/2112466
關(guān)注GitHub今日熱榜,專注挖掘好用的開發(fā)工具,致力于分享優(yōu)質(zhì)高效的工具、資源、插件等,助力開發(fā)者成長!
點(diǎn)個(gè)在看 你最好看
