簡介:
當我們在做項目的時候,往往需要定義大量的JavaBean,而且每一個實體的每一個成員變量都需要定義getter/setter方法,顯得比較繁瑣。而Lombok通過注解的方式幫助我們進行去繁瑣化,提供一個簡潔編程方式。Lombok的注解並沒有使用到Java的反射機制,而是通過一些奇淫技巧,在代碼編譯時期動態的將注解轉換為具體的代碼。
依賴添加:
>>Gradle:
provided group: 'org.projectlombok', name: 'lombok', version: '1.16.18'
>>Maven::
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.18</version> <scope>provided</scope> </dependency>
Lombok Idea插件安裝:
>>Step.1、Setting -> plugins -> Browse repositories -> lombok search -> install

>>Step.2、點擊右邊的install,然后重啟Intellij Idea即可。ps:該截圖是因為已經安裝完畢,所以沒有install按鈕

>>Step.3、Lombok安裝完成之后,當第一次啟動項目的時候,會報:Lombok Requires Annotation Processing的錯誤,如下所示:

>>Step.4、這個時候是需要為項目開啟 Enable annotation processing功能,可以點擊藍色的字直接打開編輯頁面,也可以通過setting -> Build,Execution,Deployment -> Annotation Processors打開編輯頁面,然后選擇 Enable annotation processing前面的勾勾,點擊Apply重啟Idea即可

Lombok注解:
- @NonNull:在參數中使用時,如果調用時傳了null值,會拋出空異常
- @Cleanup:關閉流、連接點
- @Setter:注解在類上,為所有的屬性添加set方法、注解在屬性上為該屬性提供set方法
- @Getter:注解在類上,為所有的屬性添加get方法、注解在屬性上為該屬性提供get方法
- @ToString:創建一個toString方法
- @EqualsAndHashCode:重寫equals和hashCode方法
- @NoArgsConstructor:創建一個無參構造函數
- @AllArgsConstructor:創建一個全參構造函數
- @RequiredArgsConstructor:創建一個包含所有非null值字段的構造函數
- @Data:注解在類上,將類提供的所有屬性都添加get、set方法,並添加equals、canEquals、hashCode和toString方法
- @Value: 所有字段默認為私有和最終的,並且不會生成setter,是@Data的不可變變體
- @Builder:使用builder模式創建對象。ps:builder模式請參見effective java第一章或google
- @SneakyThrows:代替異常捕獲,只需要將異常例外(任意數量的)傳遞給該注釋
- @Synchronized:用於方法,可以鎖定指定的對象,如果不指定,則默認創建一個對象鎖定
- @Getter(lazy=true):您可以讓lombok生成一個getter,它會在第一次調用getter時計算一次值,並從此開始緩存。 如果計算該值需要大量的CPU,或者該值需要大量內存,則這可能很有用。 要使用此功能,請創建一個私有最終變量,使用運行費用較高的表達式對其進行初始化,並使用@Getter(lazy = true)對您的字段進行注釋。 該字段將隱藏其余的代碼,並且當第一次調用getter時,表達式將被評估不超過一次。 沒有神奇的標記值(即使您的昂貴計算結果為空,結果被緩存),並且昂貴的計算不需要線程安全,因為lombok負責鎖定。
- @Log:作用於類,創建一個Log屬性
官方deamo:
這里只羅列@NonNull、@Data、@Builder、@SneakyThrows四個注解的demo,從demo可與看出將我們的代碼復雜度簡潔了不少。需要了解其它注解的demo請參考官方網站:https://projectlombok.org/features/
- @nonNull
- With Lombok
import lombok.NonNull; public class NonNullExample extends Something { private String name; public NonNullExample(@NonNull Person person) { super("Hello"); this.name = person.getName(); } }
- Vanilla Java
import lombok.NonNull; public class NonNullExample extends Something { private String name; public NonNullExample(@NonNull Person person) { super("Hello"); if (person == null) { throw new NullPointerException("person"); } this.name = person.getName(); } }
- With Lombok
- @Data
- With Lombok
import lombok.AccessLevel; import lombok.Setter; import lombok.Data; import lombok.ToString; @Data public class DataExample { private final String name; @Setter(AccessLevel.PACKAGE) private int age; private double score; private String[] tags; @ToString(includeFieldNames=true) @Data(staticConstructor="of") public static class Exercise<T> { private final String name; private final T value; } }
- Vanilla Java
import java.util.Arrays; public class DataExample { private final String name; private int age; private double score; private String[] tags; public DataExample(String name) { this.name = name; } public String getName() { return this.name; } void setAge(int age) { this.age = age; } public int getAge() { return this.age; } public void setScore(double score) { this.score = score; } public double getScore() { return this.score; } public String[] getTags() { return this.tags; } public void setTags(String[] tags) { this.tags = tags; } @Override public String toString() { return "DataExample(" + this.getName() + ", " + this.getAge() + ", " + this.getScore() + ", " + Arrays.deepToString(this.getTags()) + ")"; } protected boolean canEqual(Object other) { return other instanceof DataExample; } @Override public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof DataExample)) return false; DataExample other = (DataExample) o; if (!other.canEqual((Object)this)) return false; if (this.getName() == null ? other.getName() != null : !this.getName().equals(other.getName())) return false; if (this.getAge() != other.getAge()) return false; if (Double.compare(this.getScore(), other.getScore()) != 0) return false; if (!Arrays.deepEquals(this.getTags(), other.getTags())) return false; return true; } @Override public int hashCode() { final int PRIME = 59; int result = 1; final long temp1 = Double.doubleToLongBits(this.getScore()); result = (result*PRIME) + (this.getName() == null ? 43 : this.getName().hashCode()); result = (result*PRIME) + this.getAge(); result = (result*PRIME) + (int)(temp1 ^ (temp1 >>> 32)); result = (result*PRIME) + Arrays.deepHashCode(this.getTags()); return result; } public static class Exercise<T> { private final String name; private final T value; private Exercise(String name, T value) { this.name = name; this.value = value; } public static <T> Exercise<T> of(String name, T value) { return new Exercise<T>(name, value); } public String getName() { return this.name; } public T getValue() { return this.value; } @Override public String toString() { return "Exercise(name=" + this.getName() + ", value=" + this.getValue() + ")"; } protected boolean canEqual(Object other) { return other instanceof Exercise; } @Override public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof Exercise)) return false; Exercise<?> other = (Exercise<?>) o; if (!other.canEqual((Object)this)) return false; if (this.getName() == null ? other.getValue() != null : !this.getName().equals(other.getName())) return false; if (this.getValue() == null ? other.getValue() != null : !this.getValue().equals(other.getValue())) return false; return true; } @Override public int hashCode() { final int PRIME = 59; int result = 1; result = (result*PRIME) + (this.getName() == null ? 43 : this.getName().hashCode()); result = (result*PRIME) + (this.getValue() == null ? 43 : this.getValue().hashCode()); return result; } } }
- With Lombok
- @Builder
- With Lombok
import lombok.Builder; import lombok.Singular; import java.util.Set; @Builder public class BuilderExample { @Builder.Default private long created = System.currentTimeMillis(); private String name; private int age; @Singular private Set<String> occupations; }
- Vanilla Java
import java.util.Set; public class BuilderExample { private long created; private String name; private int age; private Set<String> occupations; BuilderExample(String name, int age, Set<String> occupations) { this.name = name; this.age = age; this.occupations = occupations; } private static long $default$created() { return System.currentTimeMillis(); } public static BuilderExampleBuilder builder() { return new BuilderExampleBuilder(); } public static class BuilderExampleBuilder { private long created; private boolean created$set; private String name; private int age; private java.util.ArrayList<String> occupations; BuilderExampleBuilder() { } public BuilderExampleBuilder created(long created) { this.created = created; this.created$set = true; return this; } public BuilderExampleBuilder name(String name) { this.name = name; return this; } public BuilderExampleBuilder age(int age) { this.age = age; return this; } public BuilderExampleBuilder occupation(String occupation) { if (this.occupations == null) { this.occupations = new java.util.ArrayList<String>(); } this.occupations.add(occupation); return this; } public BuilderExampleBuilder occupations(Collection<? extends String> occupations) { if (this.occupations == null) { this.occupations = new java.util.ArrayList<String>(); } this.occupations.addAll(occupations); return this; } public BuilderExampleBuilder clearOccupations() { if (this.occupations != null) { this.occupations.clear(); } return this; } public BuilderExample build() { // complicated switch statement to produce a compact properly sized immutable set omitted. Set<String> occupations = ...; return new BuilderExample(created$set ? created : BuilderExample.$default$created(), name, age, occupations); } @java.lang.Override public String toString() { return "BuilderExample.BuilderExampleBuilder(created = " + this.created + ", name = " + this.name + ", age = " + this.age + ", occupations = " + this.occupations + ")"; } } }
- With Lombok
- @SneakyThrows
- With Lombok
import lombok.SneakyThrows; public class SneakyThrowsExample implements Runnable { @SneakyThrows(UnsupportedEncodingException.class) public String utf8ToString(byte[] bytes) { return new String(bytes, "UTF-8"); } @SneakyThrows public void run() { throw new Throwable(); } }
- Vanilla Java
import lombok.Lombok; public class SneakyThrowsExample implements Runnable { public String utf8ToString(byte[] bytes) { try { return new String(bytes, "UTF-8"); } catch (UnsupportedEncodingException e) { throw Lombok.sneakyThrow(e); } } public void run() { try { throw new Throwable(); } catch (Throwable t) { throw Lombok.sneakyThrow(t); } } }
- With Lombok
Lombok之奇淫技:
其奇就奇在是通過對注解的解析來進行編譯時增強的。說到解析我們得對比區分下:
運行時解析:
運行時解析,我們必須使用元注解的RetentionPolicy.RUNTIME,這樣我們得程序在運行時,我們才可以通過反射的方式拿到該注解。java.lang.reflect反射包中提過了一個接口AnnotatedElment,該接口定義了獲取注解信息的幾個方法,Class、Constructor、Field、Method、Package等都實現了該接口。
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass); <T extends Annotation> T getAnnotation(Class<T> annotationClass); Annotation[] getAnnotations(); Annotation[] getDeclaredAnnotations();
編譯時解析:
編譯時解析涉及到兩種規則(其實自己也沒搞懂,這里引用一下別人的區分)
a. Annotation Processing Tool
apt自JDK5產生,JDK7已標記為過期,不推薦使用,JDK8中已徹底刪除,自JDK6開始,可以使用Pluggable Annotation Processing API來替換它,apt被替換主要有2點原因:
- api都在com.sun.mirror非標准包下
- 沒有集成到javac中,需要額外運行
apt的更多介紹可以參見這里。
b. Pluggable Annotation Processing API
JSR 269,自JDK6加入,作為apt的替代方案,它解決了apt的兩個問題,javac在執行的時候會調用實現了該API的程序,這樣我們就可以對編譯器做一些增強,這時javac執行的過程如下:
Lombok就是使用這種方式實現的,有興趣的話可以去看看其Lombok源碼,對應注解的實現都在HandleXXX中,比如@Getter注解的實現是HandleGetter.handle()。還有一些其它類庫使用這種方式實現,比如Google Auto、Dagger等等。
Lombok問題:
- 無法支持多種參數構造器的重載
- 奇淫巧技,使用會有爭議
參考文章:
- https://blog.csdn.net/ghsau/article/details/52334762
- https://blog.mythsman.com/2017/12/19/1/
- https://projectlombok.org/features/experimental/all
- https://yq.aliyun.com/articles/59972#11
