閱讀更多關於 Angular、TypeScript、Node.js/Java 、Spring 等技術文章,歡迎訪問個人我的博客 —— 全棧修仙之路
Lombok 是一款 Java 開發插件,使得 Java 開發者能夠經過其定義的一些註解來消除業務工程中冗長和繁瑣的代碼,尤爲對於簡單的 Java 模型對象(POJO)。在開發環境中使用 Lombok 插件後,Java 開發人員能夠節省出重複構建,諸如 hashCode 和 equals 這樣的方法以及各類業務對象模型的 accessor 和 toString 等方法的大量時間。對於這些方法,Lombok 可以在編譯源代碼期間自動幫咱們生成這些方法,但並不會像反射那樣下降程序的性能。java
在 build.gradle
文件中添加 lombok 依賴:git
dependencies { compileOnly 'org.projectlombok:lombok:1.18.10' annotationProcessor 'org.projectlombok:lombok:1.18.10' }
在 Maven 項目的 pom.xml
文件中添加 lombok 依賴:github
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.10</version> <scope>provided</scope> </dependency>
假設在 lib
目錄中已經存在 lombok.jar
,而後設置 javac 任務:spring
<javac srcdir="src" destdir="build" source="1.8"> <classpath location="lib/lombok.jar" /> </javac>
因爲 Lombok 僅在編譯階段生成代碼,因此使用 Lombok 註解的源代碼,在 IDE 中會被高亮顯示錯誤,針對這個問題能夠經過安裝 IDE 對應的插件來解決。這裏不詳細展開,具體的安裝方式能夠參考 Setting up Lombok with Eclipse and IntelliJ 這篇文章。apache
注意:如下示例所使用的 Lombok 版本是 1.18.10
你可使用 @Getter
或 @Setter
註釋任何類或字段,Lombok 會自動生成默認的 getter/setter 方法。安全
@Target({ElementType.FIELD, ElementType.TYPE}) @Retention(RetentionPolicy.SOURCE) public @interface Getter { // 若getter方法非public的話,能夠設置可訪問級別 lombok.AccessLevel value() default lombok.AccessLevel.PUBLIC; AnyAnnotation[] onMethod() default {}; // 是否啓用延遲初始化 boolean lazy() default false; }
@Target({ElementType.FIELD, ElementType.TYPE}) @Retention(RetentionPolicy.SOURCE) public @interface Setter { // 若setter方法非public的話,能夠設置可訪問級別 lombok.AccessLevel value() default lombok.AccessLevel.PUBLIC; AnyAnnotation[] onMethod() default {}; AnyAnnotation[] onParam() default {}; }
package com.semlinker.lombok; @Getter @Setter public class GetterAndSetterDemo { String firstName; String lastName; LocalDate dateOfBirth; }
以上代碼通過 Lombok 編譯後,會生成以下代碼:springboot
package com.semlinker.lombok; public class GetterAndSetterDemo { String firstName; String lastName; LocalDate dateOfBirth; public GetterAndSetterDemo() { } // 省略其它setter和getter方法 public String getFirstName() { return this.firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } }
@Getter
註解支持一個 lazy 屬性,該屬性默認爲 false。當設置爲 true 時,會啓用延遲初始化,即當首次調用 getter 方法時才進行初始化。ide
package com.semlinker.lombok; public class LazyGetterDemo { public static void main(String[] args) { LazyGetterDemo m = new LazyGetterDemo(); System.out.println("Main instance is created"); m.getLazy(); } @Getter private final String notLazy = createValue("not lazy"); @Getter(lazy = true) private final String lazy = createValue("lazy"); private String createValue(String name) { System.out.println("createValue(" + name + ")"); return null; } }
以上代碼通過 Lombok 編譯後,會生成以下代碼:函數
package com.semlinker.lombok; public class LazyGetterDemo { private final String notLazy = this.createValue("not lazy"); private final AtomicReference<Object> lazy = new AtomicReference(); // 已省略部分代碼 public String getNotLazy() { return this.notLazy; } public String getLazy() { Object value = this.lazy.get(); if (value == null) { synchronized(this.lazy) { value = this.lazy.get(); if (value == null) { String actualValue = this.createValue("lazy"); value = actualValue == null ? this.lazy : actualValue; this.lazy.set(value); } } } return (String)((String)(value == this.lazy ? null : value)); } }
經過以上代碼可知,調用 getLazy 方法時,若發現 value 爲 null,則會在同步代碼塊中執行初始化操做。工具
使用 @NoArgsConstructor
註解能夠爲指定類,生成默認的構造函數,@NoArgsConstructor
註解的定義以下:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.SOURCE) public @interface NoArgsConstructor { // 若設置該屬性,將會生成一個私有的構造函數且生成一個staticName指定的靜態方法 String staticName() default ""; AnyAnnotation[] onConstructor() default {}; // 設置生成構造函數的訪問級別,默認是public AccessLevel access() default lombok.AccessLevel.PUBLIC; // 若設置爲true,則初始化全部final的字段爲0/null/false boolean force() default false; }
package com.semlinker.lombok; @NoArgsConstructor(staticName = "getInstance") public class NoArgsConstructorDemo { private long id; private String name; private int age; }
以上代碼通過 Lombok 編譯後,會生成以下代碼:
package com.semlinker.lombok; public class NoArgsConstructorDemo { private long id; private String name; private int age; private NoArgsConstructorDemo() { } public static NoArgsConstructorDemo getInstance() { return new NoArgsConstructorDemo(); } }
使用 @AllArgsConstructor
註解能夠爲指定類,生成包含全部成員的構造函數,@AllArgsConstructor
註解的定義以下:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.SOURCE) public @interface AllArgsConstructor { // 若設置該屬性,將會生成一個私有的構造函數且生成一個staticName指定的靜態方法 String staticName() default ""; AnyAnnotation[] onConstructor() default {}; // 設置生成構造函數的訪問級別,默認是public AccessLevel access() default lombok.AccessLevel.PUBLIC; }
package com.semlinker.lombok; @AllArgsConstructor public class AllArgsConstructorDemo { private long id; private String name; private int age; }
以上代碼通過 Lombok 編譯後,會生成以下代碼:
package com.semlinker.lombok; public class AllArgsConstructorDemo { private long id; private String name; private int age; public AllArgsConstructorDemo(long id, String name, int age) { this.id = id; this.name = name; this.age = age; } }
使用 @RequiredArgsConstructor
註解能夠爲指定類必需初始化的成員變量,如 final 成員變量,生成對應的構造函數,@RequiredArgsConstructor
註解的定義以下:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.SOURCE) public @interface RequiredArgsConstructor { // 若設置該屬性,將會生成一個私有的構造函數且生成一個staticName指定的靜態方法 String staticName() default ""; AnyAnnotation[] onConstructor() default {}; // 設置生成構造函數的訪問級別,默認是public AccessLevel access() default lombok.AccessLevel.PUBLIC; }
package com.semlinker.lombok; @RequiredArgsConstructor public class RequiredArgsConstructorDemo { private final long id; private String name; private int age; }
以上代碼通過 Lombok 編譯後,會生成以下代碼:
package com.semlinker.lombok; public class RequiredArgsConstructorDemo { private final long id; private String name; private int age; public RequiredArgsConstructorDemo(long id) { this.id = id; } }
使用 @EqualsAndHashCode
註解能夠爲指定類生成 equals 和 hashCode 方法, @EqualsAndHashCode
註解的定義以下:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.SOURCE) public @interface EqualsAndHashCode { // 指定在生成的equals和hashCode方法中須要排除的字段列表 String[] exclude() default {}; // 顯式列出用於identity的字段,通常狀況下non-static,non-transient字段會被用於identity String[] of() default {}; // 標識在執行字段計算前,是否調用父類的equals和hashCode方法 boolean callSuper() default false; boolean doNotUseGetters() default false; AnyAnnotation[] onParam() default {}; @Deprecated @Retention(RetentionPolicy.SOURCE) @Target({}) @interface AnyAnnotation {} @Target(ElementType.FIELD) @Retention(RetentionPolicy.SOURCE) public @interface Exclude {} @Target({ElementType.FIELD, ElementType.METHOD}) @Retention(RetentionPolicy.SOURCE) public @interface Include { String replaces() default ""; } }
package com.semlinker.lombok; @EqualsAndHashCode public class EqualsAndHashCodeDemo { String firstName; String lastName; LocalDate dateOfBirth; }
以上代碼通過 Lombok 編譯後,會生成以下代碼:
package com.semlinker.lombok; public class EqualsAndHashCodeDemo { String firstName; String lastName; LocalDate dateOfBirth; public EqualsAndHashCodeDemo() { } public boolean equals(Object o) { if (o == this) { return true; } else if (!(o instanceof EqualsAndHashCodeDemo)) { return false; } else { EqualsAndHashCodeDemo other = (EqualsAndHashCodeDemo)o; if (!other.canEqual(this)) { return false; } else { // 已省略大量代碼 } } public int hashCode() { int PRIME = true; int result = 1; Object $firstName = this.firstName; int result = result * 59 + ($firstName == null ? 43 : $firstName.hashCode()); Object $lastName = this.lastName; result = result * 59 + ($lastName == null ? 43 : $lastName.hashCode()); Object $dateOfBirth = this.dateOfBirth; result = result * 59 + ($dateOfBirth == null ? 43 : $dateOfBirth.hashCode()); return result; } }
使用 @ToString
註解能夠爲指定類生成 toString 方法, @ToString
註解的定義以下:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.SOURCE) public @interface ToString { // 打印輸出時是否包含字段的名稱 boolean includeFieldNames() default true; // 列出打印輸出時,須要排除的字段列表 String[] exclude() default {}; // 顯式的列出須要打印輸出的字段列表 String[] of() default {}; // 打印輸出的結果中是否包含父類的toString方法的返回結果 boolean callSuper() default false; boolean doNotUseGetters() default false; boolean onlyExplicitlyIncluded() default false; @Target(ElementType.FIELD) @Retention(RetentionPolicy.SOURCE) public @interface Exclude {} @Target({ElementType.FIELD, ElementType.METHOD}) @Retention(RetentionPolicy.SOURCE) public @interface Include { int rank() default 0; String name() default ""; } }
package com.semlinker.lombok; @ToString(exclude = {"dateOfBirth"}) public class ToStringDemo { String firstName; String lastName; LocalDate dateOfBirth; }
以上代碼通過 Lombok 編譯後,會生成以下代碼:
package com.semlinker.lombok; public class ToStringDemo { String firstName; String lastName; LocalDate dateOfBirth; public ToStringDemo() { } public String toString() { return "ToStringDemo(firstName=" + this.firstName + ", lastName=" + this.lastName + ")"; } }
@Data
註解與同時使用如下的註解的效果是同樣的:
@Data
註解的定義以下:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.SOURCE) public @interface Data { String staticConstructor() default ""; }
package com.semlinker.lombok; @Data public class DataDemo { private Long id; private String summary; private String description; }
以上代碼通過 Lombok 編譯後,會生成以下代碼:
package com.semlinker.lombok; public class DataDemo { private Long id; private String summary; private String description; public DataDemo() { } // 省略summary和description成員屬性的setter和getter方法 public Long getId() { return this.id; } public void setId(Long id) { this.id = id; } public boolean equals(Object o) { if (o == this) { return true; } else if (!(o instanceof DataDemo)) { return false; } else { DataDemo other = (DataDemo)o; if (!other.canEqual(this)) { return false; } else { // 已省略大量代碼 } } } protected boolean canEqual(Object other) { return other instanceof DataDemo; } public int hashCode() { int PRIME = true; int result = 1; Object $id = this.getId(); int result = result * 59 + ($id == null ? 43 : $id.hashCode()); Object $summary = this.getSummary(); result = result * 59 + ($summary == null ? 43 : $summary.hashCode()); Object $description = this.getDescription(); result = result * 59 + ($description == null ? 43 : $description.hashCode()); return result; } public String toString() { return "DataDemo(id=" + this.getId() + ", summary=" + this.getSummary() + ", description=" + this.getDescription() + ")"; } }
若你將 @Log
的變體放在類上(適用於你所使用的日誌記錄系統的任何一種);以後,你將擁有一個靜態的 final log 字段,而後你就可使用該字段來輸出日誌。
private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName());
private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(LogExample.class);
private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExample.class);
private static final org.slf4j.ext.XLogger log = org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);
private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(LogExample.class);
@Synchronized
是同步方法修飾符的更安全的變體。與 synchronized
同樣,該註解只能應用在靜態和實例方法上。它的操做相似於 synchronized
關鍵字,可是它鎖定在不一樣的對象上。 synchronized
關鍵字應用在實例方法時,鎖定的是 this 對象,而應用在靜態方法上鎖定的是類對象。對於 @Synchronized 註解聲明的方法來講,它鎖定的是 $LOCK
或 $lock
。@Synchronized
註解的定義以下:
@Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Synchronized { // 指定鎖定的字段名稱 String value() default ""; }
package com.semlinker.lombok; public class SynchronizedDemo { private final Object readLock = new Object(); @Synchronized public static void hello() { System.out.println("world"); } @Synchronized public int answerToLife() { return 42; } @Synchronized("readLock") public void foo() { System.out.println("bar"); } }
以上代碼通過 Lombok 編譯後,會生成以下代碼:
package com.semlinker.lombok; public class SynchronizedDemo { private static final Object $LOCK = new Object[0]; private final Object $lock = new Object[0]; private final Object readLock = new Object(); public SynchronizedDemo() { } public static void hello() { synchronized($LOCK) { System.out.println("world"); } } public int answerToLife() { synchronized(this.$lock) { return 42; } } public void foo() { synchronized(this.readLock) { System.out.println("bar"); } } }
使用 @Builder
註解能夠爲指定類實現建造者模式,該註解能夠放在類、構造函數或方法上。@Builder
註解的定義以下:
@Target({TYPE, METHOD, CONSTRUCTOR}) @Retention(SOURCE) public @interface Builder { @Target(FIELD) @Retention(SOURCE) public @interface Default {} // 建立新的builder實例的方法名稱 String builderMethodName() default "builder"; // 建立Builder註解類對應實例的方法名稱 String buildMethodName() default "build"; // builder類的名稱 String builderClassName() default ""; boolean toBuilder() default false; AccessLevel access() default lombok.AccessLevel.PUBLIC; @Target({FIELD, PARAMETER}) @Retention(SOURCE) public @interface ObtainVia { String field() default ""; String method() default ""; boolean isStatic() default false; } }
package com.semlinker.lombok; @Builder public class BuilderDemo { private final String firstname; private final String lastname; private final String email; }
以上代碼通過 Lombok 編譯後,會生成以下代碼:
package com.semlinker.lombok; public class BuilderDemo { private final String firstname; private final String lastname; private final String email; BuilderDemo(String firstname, String lastname, String email) { this.firstname = firstname; this.lastname = lastname; this.email = email; } public static BuilderDemo.BuilderDemoBuilder builder() { return new BuilderDemo.BuilderDemoBuilder(); } public static class BuilderDemoBuilder { private String firstname; private String lastname; private String email; BuilderDemoBuilder() { } public BuilderDemo.BuilderDemoBuilder firstname(String firstname) { this.firstname = firstname; return this; } public BuilderDemo.BuilderDemoBuilder lastname(String lastname) { this.lastname = lastname; return this; } public BuilderDemo.BuilderDemoBuilder email(String email) { this.email = email; return this; } public BuilderDemo build() { return new BuilderDemo(this.firstname, this.lastname, this.email); } public String toString() { return "BuilderDemo.BuilderDemoBuilder(firstname=" + this.firstname + ", lastname=" + this.lastname + ", email=" + this.email + ")"; } } }
@SneakyThrows
註解用於自動拋出已檢查的異常,而無需在方法中使用 throw 語句顯式拋出。@SneakyThrows
註解的定義以下:
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR}) @Retention(RetentionPolicy.SOURCE) public @interface SneakyThrows { // 設置你但願向上拋的異常類 Class<? extends Throwable>[] value() default java.lang.Throwable.class; }
package com.semlinker.lombok; public class SneakyThrowsDemo { @SneakyThrows @Override protected Object clone() { return super.clone(); } }
以上代碼通過 Lombok 編譯後,會生成以下代碼:
package com.semlinker.lombok; public class SneakyThrowsDemo { public SneakyThrowsDemo() { } protected Object clone() { try { return super.clone(); } catch (Throwable var2) { throw var2; } } }
你能夠在方法或構造函數的參數上使用 @NonNull
註解,它將會爲你自動生成非空校驗語句。 @NonNull
註解的定義以下:
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE, ElementType.TYPE_USE}) @Retention(RetentionPolicy.CLASS) @Documented public @interface NonNull { }
package com.semlinker.lombok; public class NonNullDemo { @Getter @Setter @NonNull private String name; }
以上代碼通過 Lombok 編譯後,會生成以下代碼:
package com.semlinker.lombok; public class NonNullDemo { @NonNull private String name; public NonNullDemo() { } @NonNull public String getName() { return this.name; } public void setName(@NonNull String name) { if (name == null) { throw new NullPointerException("name is marked non-null but is null"); } else { this.name = name; } } }
@Clean
註解用於自動管理資源,用在局部變量以前,在當前變量範圍內即將執行完畢退出以前會自動清理資源,自動生成 try-finally
這樣的代碼來關閉流。
@Target(ElementType.LOCAL_VARIABLE) @Retention(RetentionPolicy.SOURCE) public @interface Cleanup { // 設置用於執行資源清理/回收的方法名稱,對應方法不能包含任何參數,默認名稱爲close。 String value() default "close"; }
package com.semlinker.lombok; public class CleanupDemo { public static void main(String[] args) throws IOException { @Cleanup InputStream in = new FileInputStream(args[0]); @Cleanup OutputStream out = new FileOutputStream(args[1]); byte[] b = new byte[10000]; while (true) { int r = in.read(b); if (r == -1) break; out.write(b, 0, r); } } }
以上代碼通過 Lombok 編譯後,會生成以下代碼:
package com.semlinker.lombok; public class CleanupDemo { public CleanupDemo() { } public static void main(String[] args) throws IOException { FileInputStream in = new FileInputStream(args[0]); try { FileOutputStream out = new FileOutputStream(args[1]); try { byte[] b = new byte[10000]; while(true) { int r = in.read(b); if (r == -1) { return; } out.write(b, 0, r); } } finally { if (Collections.singletonList(out).get(0) != null) { out.close(); } } } finally { if (Collections.singletonList(in).get(0) != null) { in.close(); } } } }
在類的字段上應用 @With
註解以後,將會自動生成一個 withFieldName(newValue)
的方法,該方法會基於 newValue 調用相應構造函數,建立一個當前類對應的實例。 @With
註解的定義以下:
@Target({ElementType.FIELD, ElementType.TYPE}) @Retention(RetentionPolicy.SOURCE) public @interface With { AccessLevel value() default AccessLevel.PUBLIC; With.AnyAnnotation[] onMethod() default {}; With.AnyAnnotation[] onParam() default {}; @Deprecated @Retention(RetentionPolicy.SOURCE) @Target({}) public @interface AnyAnnotation { } }
public class WithDemo { @With(AccessLevel.PROTECTED) @NonNull private final String name; @With private final int age; public WithDemo(String name, int age) { if (name == null) throw new NullPointerException(); this.name = name; this.age = age; } }
以上代碼通過 Lombok 編譯後,會生成以下代碼:
public class WithDemo { @NonNull private final String name; private final int age; public WithDemo(String name, int age) { if (name == null) { throw new NullPointerException(); } else { this.name = name; this.age = age; } } protected WithDemo withName(@NonNull String name) { if (name == null) { throw new NullPointerException("name is marked non-null but is null"); } else { return this.name == name ? this : new WithDemo(name, this.age); } } public WithDemo withAge(int age) { return this.age == age ? this : new WithDemo(this.name, age); } }
val 用在局部變量前面,至關於將變量聲明爲 final,此外 Lombok 在編譯時還會自動進行類型推斷。val 的使用示例:
public class ValExample { public String example() { val example = new ArrayList<String>(); example.add("Hello, World!"); val foo = example.get(0); return foo.toLowerCase(); } public void example2() { val map = new HashMap<Integer, String>(); map.put(0, "zero"); map.put(5, "five"); for (val entry : map.entrySet()) { System.out.printf("%d: %s\n", entry.getKey(), entry.getValue()); } } }
以上代碼等價於:
public class ValExample { public String example() { final ArrayList<String> example = new ArrayList<String>(); example.add("Hello, World!"); final String foo = example.get(0); return foo.toLowerCase(); } public void example2() { final HashMap<Integer, String> map = new HashMap<Integer, String>(); map.put(0, "zero"); map.put(5, "five"); for (final Map.Entry<Integer, String> entry : map.entrySet()) { System.out.printf("%d: %s\n", entry.getKey(), entry.getValue()); } } }
至此功能強大的 Lombok 工具就介紹完了。若你對於它的實現原理感興趣的話,建議閱讀猿碼道大佬 十分鐘搞懂Lombok使用與原理 這篇文章。
示例項目地址: Github - springboot2-lombok