Java開發(fā)神器Lombok的使用與原理

在面向?qū)ο缶幊讨斜夭豢缮傩枰诖a中定義對象模型,而在基于Java的業(yè)務(wù)平臺開發(fā)實(shí)踐中尤其如此。相信大家在平時開發(fā)中也深有感觸,本來是沒有多少代碼開發(fā)量的,但是因?yàn)槎x的業(yè)務(wù)模型對象比較多,而需要重復(fù)寫Getter/Setter、構(gòu)造器方法、字符串輸出的ToString方法和Equals/HashCode方法等。那么是否一款插件或工具能夠替大家完成這些繁瑣的操作呢?本文將向大家介紹一款在Eclipse/Intellij IDEA主流的開發(fā)環(huán)境中都可以使用的Java開發(fā)神器,同時簡要地介紹下其背后自定義注解的原理。

Lombok的簡介

Lombok是一款Java開發(fā)插件,使得Java開發(fā)者可以通過其定義的一些注解來消除業(yè)務(wù)工程中冗長和繁瑣的代碼,尤其對于簡單的Java模型對象(POJO)。在開發(fā)環(huán)境中使用Lombok插件后,Java開發(fā)人員可以節(jié)省出重復(fù)構(gòu)建,諸如hashCode和equals這樣的方法以及各種業(yè)務(wù)對象模型的accessor和ToString等方法的大量時間。對于這些方法,它能夠在編譯源代碼期間自動幫我們生成這些方法,并沒有如反射那樣降低程序的性能。

在Intellij中安裝Lombok的插件

想要體驗(yàn)一把Lombok的話,得先在自己的開發(fā)環(huán)境中安裝上對應(yīng)的插件。下面先為大家展示下如何在Intellij中安裝上Lombok插件。

通過IntelliJ的插件中心尋找Lombok

image

從Intellij插件中心安裝Lombok

image

另外需要注意的是,在使用lombok注解的時候記得要導(dǎo)入lombok.jar包到工程,如果使用的是Maven的工程項(xiàng)目的話,要在其pom.xml中添加依賴如下:

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.16.8</version>
</dependency>

好了,就這么幾步后就可以在Java工程中開始用Lombok這款開發(fā)神器了。下文將會給大家介紹Lombok中一些注解的使用方法,讓大家對如何用這些注解有一個大致的了解。

Lombok注解使用方法

Lombok常用注解介紹

下面先來看下Lombok中主要幾個常用注解介紹:

image

Lombok的基本使用示例

(1) Val可以將變量申明是final類型。

public   static void main(String[] args) {
    val setVar = new HashSet<String>();
    val listsVar = new   ArrayList<String>();
    val mapVar = new HashMap<String,   String>();

    //=>上面代碼相當(dāng)于如下:
    final Set<String> setVar2 = new   HashSet<>();
    final List<String> listsVar2 = new   ArrayList<>();
    final Map<String, String> maps2 =   new HashMap<>();
}

(2) @NonNull注解能夠?yàn)榉椒ɑ驑?gòu)造函數(shù)的參數(shù)提供非空檢查。

public void notNullExample(@NonNull String string) { 
    string.length(); 
} 
//=>相當(dāng)于 
public void notNullExample(String string) { 
    if (string != null) { 
        string.length(); 
    } else { 
        throw new NullPointerException("null"); 
    } 
}

(3) @Cleanup注解能夠自動釋放資源。

image.png

(4) @Getter/@Setter注解可以針對類的屬性字段自動生成Get/Set方法。

image.png

(5) @ToString注解,為使用該注解的類生成一個toString方法,默認(rèn)的toString格式為:ClassName(fieldName= fieleValue ,fieldName1=fieleValue)。

@ToString(callSuper=true,exclude="someExcludedField")
public   class Demo extends Bar {

    private boolean someBoolean = true;
    private String someStringField;
    private float someExcludedField;

}

//上面代碼相當(dāng)于如下:

public   class Demo extends Bar {

    private boolean someBoolean = true;
    private String someStringField;
    private float someExcludedField;
   
    @ Override
    public String toString() {
        return "Foo(super=" +   super.toString() +
            ", someBoolean=" +   someBoolean +
            ", someStringField=" +   someStringField + ")";
    }
}

(6) @EqualsAndHashCode注解,為使用該注解的類自動生成equals和hashCode方法。

微信截圖_20180516143922.png

(7) @NoArgsConstructor, @RequiredArgsConstructor, @AllArgsConstructor,這幾個注解分別為類自動生成了無參構(gòu)造器、指定參數(shù)的構(gòu)造器和包含所有參數(shù)的構(gòu)造器。

@RequiredArgsConstructor(staticName = "of") 
@AllArgsConstructor(access = AccessLevel.PROTECTED) 
public class ConstructorExample<T> { 

  private int x, y; 
  @NonNull private T description; 
  
  @NoArgsConstructor 
  public static class NoArgsExample { 
    @NonNull private String field; 
  } 

}

//上面代碼相當(dāng)于如下:
@RequiredArgsConstructor(staticName = "of") 
@AllArgsConstructor(access = AccessLevel.PROTECTED) 
public class ConstructorExample<T> { 

  private int x, y; 
  @NonNull private T description; 

  @NoArgsConstructor 
  public static class NoArgsExample { 
    @NonNull private String field; 
  } 

}

public class ConstructorExample<T> { 
  private int x, y; 
  @NonNull private T description; 

  private ConstructorExample(T description) { 
    if (description == null) throw new NullPointerException("description"); 
    this.description = description; 
  } 

  public static <T> ConstructorExample<T> of(T description) { 
    return new ConstructorExample<T>(description); 
  } 

  @java.beans.ConstructorProperties({"x", "y", "description"}) 
  protected ConstructorExample(int x, int y, T description) { 
    if (description == null) throw new NullPointerException("description"); 
    this.x = x; 
    this.y = y; 
    this.description = description; 
  } 
  
  public static class NoArgsExample { 
    @NonNull private String field;
    
    public NoArgsExample() { 
    } 
  } 
}

(8) @Data注解作用比較全,其包含注解的集合 @ToString, @EqualsAndHashCode,所有字段的 @Getter和所有非final字段的 @Setter, @RequiredArgsConstructor。其示例代碼可以參考上面幾個注解的組合。

(9) @Builder注解提供了一種比較推崇的構(gòu)建值對象的方式。

(10) @Synchronized注解類似Java中的Synchronized 關(guān)鍵字,但是可以隱藏同步鎖。

public class SynchronizedExample { 

 private final Object readLock = new   Object(); 

 @Synchronized 
 public static void hello() { 
     System.out.println("world");   
 } 

 @Synchronized("readLock") 
 public void foo() { 
   System.out.println("bar"); 
 } 

//上面代碼相當(dāng)于如下:

 public class SynchronizedExample { 

  private static final Object $LOCK = new   Object[0]; 
  private final Object readLock = new   Object(); 

  public static void hello() { 
    synchronized($LOCK) { 
      System.out.println("world"); 
    } 
  }   

  public void foo() { 
    synchronized(readLock) { 
        System.out.println("bar");   
    } 
  } 

}

Lombok背后的自定義注解原理

本文在前三章節(jié)主要介紹了Lombok這款Java開發(fā)利器中各種定義注解的使用方法,但作為一個Java開發(fā)者來說光了解插件或者技術(shù)框架的用法只是做到了“知其然而不知其所以然”,如果真正掌握其背后的技術(shù)原理,看明白源碼設(shè)計理念才能真正做到“知其然知其所以然”。好了,話不多說下面進(jìn)入本章節(jié)的正題,看下Lombok背后注解的深入原理。

可能熟悉Java自定義注解的同學(xué)已經(jīng)猜到,Lombok這款插件正是依靠可插件化的Java自定義注解處理API(JSR 269: Pluggable Annotation Processing API)來實(shí)現(xiàn)在Javac編譯階段利用“Annotation Processor”對自定義的注解進(jìn)行預(yù)處理后生成真正在JVM上面執(zhí)行的“Class文件”。有興趣的同學(xué)反編譯帶有Lombok注解的類文件也就一目了然了。其大致執(zhí)行原理圖如下:

image

從上面的這個原理圖上可以看出Annotation Processing是編譯器在解析Java源代碼和生成Class文件之間的一個步驟。其中Lombok插件具體的執(zhí)行流程如下:

image

從上面的Lombok執(zhí)行的流程圖中可以看出,在Javac 解析成AST抽象語法樹之后, Lombok 根據(jù)自己編寫的注解處理器,動態(tài)地修改 AST,增加新的節(jié)點(diǎn)(即Lombok自定義注解所需要生成的代碼),最終通過分析生成JVM可執(zhí)行的字節(jié)碼Class文件。使用Annotation Processing自定義注解是在編譯階段進(jìn)行修改,而JDK的反射技術(shù)是在運(yùn)行時動態(tài)修改,兩者相比,反射雖然更加靈活一些但是帶來的性能損耗更加大。

需要更加深入理解Lombok插件的細(xì)節(jié),自己查閱其源代碼是必比可少的。對開源框架代碼比較有執(zhí)著追求的童鞋可以將Lombok的源代碼工程從github上download到本地進(jìn)行閱讀和自己調(diào)試。下圖為Lombok工程源代碼的截圖:

image

從熟悉JSR 269: Pluggable Annotation Processing API的同學(xué)可以從工程類結(jié)構(gòu)圖中發(fā)現(xiàn)AnnotationProcessor這個類是Lombok自定義注解處理的入口。該類有兩個比較重要的方法一個是init方法,另外一個是process方法。在init方法中,先用來做參數(shù)的初始化,將AnnotationProcessor類中定義的內(nèi)部類(JavacDescriptor、EcjDescriptor)先注冊到ProcessorDescriptor類型定義的列表中。其中,內(nèi)部靜態(tài)類—JavacDescriptor在其加載的時候就將 lombok.javac.apt.LombokProcessor這個類進(jìn)行對象實(shí)例化并注冊。

LombokProcessor處理器中,其中的process方法會根據(jù)優(yōu)先級來分別運(yùn)行相應(yīng)的handler處理類。Lombok中的多個自定義注解都分別有對應(yīng)的handler處理類,如下圖所示:

image

可以看出,在Lombok中對于其自定義注解進(jìn)行實(shí)際的替換、修改和處理的正是這些handler類。對于其實(shí)現(xiàn)的細(xì)節(jié)可以具體參考其中的代碼。

本文先從Lombok使用角度出發(fā),先介紹了如何在當(dāng)前主流的Java開發(fā)環(huán)境—Intellij中安裝這塊Java插件,隨后分別介紹了Lombok中幾種主要的常用注解(比如, @Data、 @CleanUp@ToString@Getter/Setter等),最后將Lombok背后Java自定義注解的原理與源代碼結(jié)合起來,介紹了Lombok插件背后具體的執(zhí)行處理流程。限于篇幅,未能對“自定義Java注解處理器”的具體實(shí)踐和原理進(jìn)行闡述,后面將另起專題篇幅進(jìn)行介紹。限于筆者的才疏學(xué)淺,對本文內(nèi)容可能還有理解不到位的地方,如有闡述不合理之處還望留言一起探討。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 47,253評論 6 342
  • 堅持原創(chuàng)分享,第7天。孩子與父母之間的關(guān)系到底應(yīng)該是什么樣的呢?我經(jīng)常聽到這樣的父母。我的孩子很不聽話。我叫他干什...
    奇峰_5114閱讀 325評論 0 0
  • 年齡越大 越不想說話 不評頭,不論足 不博古,不通今 不說東,不扯西 不打雷,不下雨 不喊冤,不叫屈 沉默不是對現(xiàn)...
    Love_999閱讀 405評論 9 6

友情鏈接更多精彩內(nèi)容