(一)介紹
Lombok項目是一個Java庫,它會自動插入您的編輯器和構建工具中,從而爲您的Java增光添彩。永遠不要再編寫另一個getter或equals方法,帶有一個註解的類將具有功能全面的生成器,自動執行日誌記錄變量等等。
(二)原理
在Lombok使用的過程中,只需要添加相應的註解,無需再爲此寫任何代碼。自動生成的代碼到底是如何產生的呢?
核心之處就是對於註解的解析上。JDK5引入了註解的同時,也提供了兩種解析方式。
運行時解析
運行時能夠解析的註解,必須將@Retention設置爲RUNTIME,這樣就可以通過反射拿到該註解。java.lang.reflect反射包中提供了一個接口AnnotatedElement,該接口定義了獲取註解信息的幾個方法,Class、Constructor、Field、Method、Package等都實現了該接口
編譯時解析
編譯時解析有兩種機制,分別簡單描述下:
1:Annotation Processing Tool
apt自JDK5產生,JDK7已標記爲過期,不推薦使用,JDK8中已徹底刪除,自JDK6開始,可以使用Pluggable Annotation Processing API來替換它,apt被替換主要有2點原因:
api都在com.sun.mirror非標準包下
沒有集成到javac中,需要額外運行
2:Pluggable Annotation Processing API
JSR 269自JDK6加入,作爲apt的替代方案,它解決了apt的兩個問題,javac在執行的時候會調用實現了該API的程序,這樣我們就可以對編譯器做一些增強,javac執行的過程如下:
Lombok本質上就是一個實現了“JSR 269 API”的程序。在使用javac的過程中,它產生作用的具體流程如下:
javac對源代碼進行分析,生成了一棵抽象語法樹(AST)
運行過程中調用實現了“JSR 269 API”的Lombok程序
此時Lombok就對第一步驟得到的AST進行處理,找到@Data註解所在類對應的語法樹(AST),然後修改該語法樹(AST),增加getter和setter方法定義的相應樹節點
javac使用修改後的抽象語法樹(AST)生成字節碼文件,即給class增加新的節點(代碼塊)
通過讀Lombok源碼,發現對應註解的實現都在HandleXXX中,比如@Getter註解的實現在HandleGetter.handle()。還有一些其它類庫使用這種方式實現,比如Google Auto、Dagger等等。
(三)Lombok安裝
1:引入依賴
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
<scope>provided</scope>
</dependency>
2:IDEA設置
由於lombok是在編譯時期生效的,因此我們需要告訴編輯器我們使用了這個工具,需要讓編輯器能夠識別,這樣我們在觀察類結構時就能夠看到實際的類代碼,如果不設置的話,那麼由於我們僅添加了某些註解,這樣是程序開發的時候編輯器是無法識別到對應的對應方法的,那麼我們就沒辦法在程序中獲取到某類的setter和getter方法了,無法做到智能提示
3:IDEA添加Lombok插件
步驟如下:
打開IDEA,選擇[File]->[Settings]->[Plugins]
點擊[Browse repositories...]
查找[lombok]安裝
重啓IDEA
4:測試
選擇一個實體類,類上添加@Data註解,IDEA按下alt+7,如果正常顯示setter和getter方法即設置成功
(四)Lombok註解
備註:想要清楚的查看Lombok的生成的代碼,可以每次設置完註解編譯該文件,查看對應的class文件即可,如果使用的IDEA的話,按下Ctrl+Shift+F9即可.然後到對應的target目錄下查看即可,該文件每次不會自動刷新,需要重新打開
1:@Getter和@Setter
這倆個註解是生成Getter和Setter方法的註解,這倆個註解可以作用到類上也可以作用到單個字段上,作用到類上表示對所有字段添加對應的方法.
其中Getter可以設置方法的訪問權限
@Getter(AccessLevel.PROTECTED)
不同的訪問權限可以通過枚舉類AccessLevel設置
注意:
1:@Getter和@Setter只針對成員變量,如果是靜態成員不會生成對應的方法
2:如果成員變量是final修飾的,只會生成get方法,不會生成set方法
3:如果不想生成某個具體成員的set和get,那麼可以通過如下設置,不提供外部訪問和設置方法
@Getter(AccessLevel.NONE)
@Setter(AccessLevel.NONE)
2:@ToString
生成toString()方法,默認會輸出所有的成員變量,不會輸出靜態成員
指定某些成員變量不輸出
@ToString(exclude = {"id", "name"})
指定變量輸出(包括輸出靜態變量)可以通過of來設置,其中desc是靜態變量
@ToString(exclude = {"id", "name"}, of = "desc")
3:@EqualsAndHashCode
該註解會自動生成equals、canEqual、hashCode三個方法,現在依次查看下每個方法
equals方法,首先判斷地址是否相同,然後判斷類型是否相同,最後依次對比每個成員變量的值是否相等
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (!(o instanceof User)) {
return false;
} else {
User other = (User)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;
}
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;
}
return true;
}
}
}
//canEqual就是判斷對象是否是當前類實例
protected boolean canEqual(Object other) {
return other instanceof User;
}
public int hashCode() {
int PRIME = true;
int result = 1;
Object $id = this.getId();
int result = result * 59 + ($id == null ? 43 : $id.hashCode());
Object $name = this.getName();
result = result * 59 + ($name == null ? 43 : $name.hashCode());
return result;
}
自定義EqualsAndHashCode,如果我們只需要判斷其中幾個字段是否相等,我們可以通過
@EqualsAndHashCode(exclude = {“name”})
@EqualsAndHashCode(of = {“id”})
來排除或者限制某些字段作爲判斷邏輯
4:@NonNull
@NonNull註解可以用在字段、方法、參數、變量、類上,主要的功能是做非空判斷,如果對應值爲空,那麼會返回一個空指針異常
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE, ElementType.TYPE_USE})
5:構造函數
@RequiredArgsConstructor會自動會以下成員變量生成對應參數的構造函數
1:被final修改的變量且沒有賦初始值的,此時不能有無參構造函數
2:被註解@NonNull修改的成員變量
@NoArgsConstructor 生成無參的構造函數
@AllArgsConstructor 生成所有參數的構造函數
6:@Data
@Data是一個全能的功能型註解,包含了以下幾個註解:
@Getter和@Setter
@ToString
@EqualsAndHashCode
@RequiredArgsConstructor
7:@Builder
@Builder註釋爲你的類生成相對略微複雜的構建器API。@Builder可以讓你以下面顯示的那樣調用你的代碼,來初始化你的實例對象:
User lisi = User.builder().id("123").name("lisi").build();
——————————————————————————————
UserBuilder id = User.builder().id("123456");
User user1 = id.name("zhangsan").build();
User user2 = id.name("lisi").build();
8:@Log
@Log 註解可以不用再在每個類內寫機械式的Logger log=xxxx了,lombok幫你搞定,通過這個註解,直接在需要寫日誌的地方用log.info(“”)就可以了
public void demo() {
log.info("開始執行demo方法");
}
9:val
當我們在創建集合或者map時需要確定返回類型,但是有了val後在接收返回類型的時候都可以使用val來接收,然後它會自編譯時根據實際數據確定對應的類型。注意val可以直接使用只要導包即可,不需要額外的註解,不限制具體的類
val list = new ArrayList<>();
val map = new HashMap<>();
list.add("AAA");
map.put("AAA", new User());
//編譯後代碼如下
ArrayList<Object> list = new ArrayList();
HashMap<Object, Object> map = new HashMap();
list.add("AAA");
map.put("AAA", new User());
10: @Cleanup
自動資源管理:沒有麻煩和安全地調用您的close()方法。使用@Cleanup以確保在代碼執行路徑退出當前作用域之前自動清除給定資源。您可以通過使用@Cleanup註解任何局部變量聲明來執行此操作.
主要功能:
1:流的自動清理關閉
2:異常自動的處理
@Cleanup FileInputStream inputStream = new FileInputStream("");
@Cleanup FileOutputStream outputStream = new FileOutputStream("");
//編譯後代碼:
public void demo() throws IOException {
FileInputStream inputStream = new FileInputStream("");
try {
FileOutputStream outputStream = new FileOutputStream("");
if (Collections.singletonList(outputStream).get(0) != null) {
outputStream.close();
}
} finally {
if (Collections.singletonList(inputStream).get(0) != null) {
inputStream.close();
} } }