Lombok教程

(一)介紹

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();
     } }  }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章