解釋
Project Lombok is a java library that automatically plugs into your editor and build tools, spicing up your java. Never write another getter or equals method again, with one annotation your class has a fully featured builder, Automate your logging variables, and much more.
不要再編寫另一個getter或equals方法,使用一個注釋,您的類有一個功能齊全的生成器,自動記錄變量,等等。
Lombok原理剖析
作為一個Java開發者來說,光了解插件或者技術框架的用法只是做到了“知其然而不知其所以然”,如果真正掌握其背后的技術原理,看明白源碼設計理念才能真正做到“知其然知其所以然”。
可能熟悉Java自定義注解的同學已經猜到,Lombok這款插件在Javac編譯階段對自定義的注解進行預處理后生成真正在JVM上面執行的“Class文件”。Lombok插件具體的執行流程如下: .
從上面的Lombok執行的流程圖中可以看出,在Javac 解析成AST抽象語法樹之后,Lombok根據自己編寫的注解處理器,動態地修改AST,增加新的節點(即Lombok自定義注解所需要生成的代碼),最終通過分析生成JVM可執行的字節碼Class文件。
使用注解處理自定義注解是在編譯階段進行修改,而JDK的反射技術是在運行時動態修改,兩者相比,反射雖然更加靈活-些但是帶來的性能損耗更加大。
需要更加深入理解Lombok插件的細節,自己查閱其源代碼是必比可少的。對開源框架代碼比較有執着追求的童鞋可以將Lombok的源代碼工程從github,上download到本地進行閱讀和自己調試。
-
lombok的官方地址: https://projectlombok.org/
-
lombok的Github地址: https://github.com/rzwitserloot/lombok
注解如何解析
在Lombok使用的過程中,只需要添加相應的注解,無需再為此寫任何代碼。自動生成的代碼到底是如何產生的呢?
核心之處就是對於注解的解析上。JDK5引入了注解的同時,也提供了兩種解析方式。
運行時解析--動態性--反射
比如:在程序運行的時候,利用反射創建了一個對象,並且修改了屬性的值,所以反射是動態的。
運行時能夠解析的注解,必須將@Retention設置為RUNTIME,這樣就可以通過反射拿到該注解。java.lang,reflect反射包中提供了一個接口AnnotatedElement,該接口定義了獲取注解信息的幾個方法,Class、 Constructor. Field、 Method、Package等都實現了該接口,對反射熟悉的朋友應該都會很熟悉這種解析方式。
編譯時解析
編譯時解析有兩種機制,分別簡單描述下:
-
1) Annotation Processing Tool
-
apt自JDK5產生,JDK7已標記為過期,不推薦使用,JDK8中已徹底刪除,沒有集成到javac中,需要額外運行。自JDK6開始,可以使用Pluggable[可插拔] Annotation Processing API來替換它,
-
-
2) Pluggable Annotation Processing API
-
[插入式注解處理API(JSR 269) ]作為apt的替代方案,它解決了apt的兩個問題,javac在執行的時候會調用實現了該API的程.序,這樣我們就可以對編譯器做一些增強。
-
Lombok本質上就是一個實現了"JSR 269 API”的程序。在使用javac的過程中,它產生作用的具體流程如下:
-
javac對源代碼進行分析,生成了一棵抽象語法樹(AST)
-
運行過程中調用實現了“JSR 269 API"的L ombok程序
-
此時L ombok就對第一步驟得到的AST進行處理 ,找到@Data注解所在類對應的語法樹(AST) , 然后修改該語法樹(AST),增加getter和setter方法定 義的相應樹節點
-
javac使用修改后的抽象語法樹(AST) 生成字節碼文件,即給class增加新的節點(代碼塊)
Lombok源碼中,對應注解的實現都在HandleXXX中,比如@Getter注解的實現時HandleGetter.handle()。
Lombok的優缺點
優點:
-
能通過注解的形式自動生成構造器、getter/setter. equals、 hashcode、 toString等方法, 提高了-一定的開發效率
-
讓代碼變得簡潔,不用過多的去關注相應的方法
-
屬性做修改時,也簡化了維護為這些屬性所生成的getter/setter方法等
缺點:
-
不支持多種參數構造器的重載
總結
Lombok雖然有很多優點,但Lombok更類似於一種IDE插件, 項目也需要依賴相應的jar包。Lombok依賴jar包是因為編譯時要用它的注解,為什么說它又類似插件?因為在使用時,eclipse或IntelliJ IDEA都需要安裝相應的插件,在編譯器編譯時通過操作AST (抽象語法樹)改變字節碼生成,變向的就是說它在改變java語法。
它不像spring的依賴注入或者mybatis的ORM-樣是運行時的特性,而是編譯時的特性。這里我個人最感覺不爽的地方就是對插件的依賴!因為Lombok只是省去了一些人工生成代碼的麻煩,但IDE都有快捷鍵來協助生成getter/setter等方法,也非常方便。
知乎上有位大神發表過對Lombok的一些看法:
這是一種低級趣味的插件,不建議使用。JAVA發展到今天, 各種插件層出不窮,如何甄別各種插件的優劣?能從架構上優化你的設計的,能提高應用程序性能的,實現高度封裝可擴展的....像lombok這種, 像這種插件,已經不僅僅是插件了,改變了你如何編寫源碼,事實上,少去了的代碼,你寫上去又如何?如果JAVA家族到處充斥這樣的東西,那只不過是一坨披着金屬顏色的屎,遲早會被其它的語言取代。
雖然話糙但理確實不糙,試想一個項目有非常多類似Lombok這樣的插件,個人覺得真的會極大的降低閱讀源代碼的舒適度。雖然非常不建議在屬性的getter/setter寫一些業務代碼, 但在多年項目的實戰中,有時通過給getter/setter加一點點業務代碼,能極大的簡化某些業務場景的代碼。所謂取舍,也許就是這時的舍棄一定的規范, 取得極大的方便。
我現在非常堅信一條理念, 任何編程語言或插件,都僅僅只是工具而已,即使工具再強大也在於用的人,就如同小米加步槍照樣能贏飛機大炮的道理一樣。結合具體業務場景和項目實際情況,無需一味追求高大上的技術,適合的才是王道。
Lombok有它的得天獨厚的優點,也有它避之不及的缺點,熟知其優缺點,在實戰中靈活運用才是王道。
配置
1、安裝插件
2、在項目中導入jar包
Lombok常用注解說明
-
@NonNull:用在方法參數前,會自動對該參數進行非空校驗,為空拋出NPE (NullPointerException
-
@Cleanup: 自動管理資源,用在局部變量之前,在當前變量范圍內即將執行完畢退出前會清理資源,生成try-fially的代碼關閉流
-
@Getter/@Setter: 用在屬性上,不用自己手寫setter和getter方法,還可指定訪問范圍
-
@ToString: 用在類上,可以自動覆寫toString方法
-
@EqualsAndHashCode: 用在類上,自動生成equals方法和hashCode方法
-
@NoArgsConstructor, @RequiredArgsConstructor and @AllArgsConstructor:用在類上,自動生成無參構造和使用所有參數的有參構造函數。
-
@Data:用在類上,相當於同時使用了@ToString、@EqualsAndHashCode、 @Getter、 @Setter
-
@RequiredArgsConstrutor這些注解 ,對POJO類十分有用.
-
@Value:用在類上,是@Data的不可變形式,相當於為屬性添加final聲明,只提供getter方法,而不提供setter方法
-
@SneakyThrows: 自動拋受檢異常,而無需顯式在方法上使用throws語句
-
@Synchronized:用在方法上,將方法聲明為同步的,並自動加鎖
-
@Getter(lazy=true): 可以替代經典的Double Check Lock樣板代碼
-
@Accessors(chain = true):開啟支持鏈式編程的寫法,比如:User user = new User(); user.setName("123").setAge(12).setbirthday("").....
常用的幾個解釋
@Data
最為常用!
該注解作用比較全,其包含注解的集合@ToString, @Equal sAndHashCode,所有字段的@Get ter和所有非final字段的@Setter, @RequiredArgsConstructor。
@Data public class Person { private Integer id; private String name; }
@NoArgsConstructor 和 @AllArgsConstructor
import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor // 空構造 @AllArgsConstructor // 有參構造(包括所有的屬性的構造器),定義有參構造后,就沒有空參構造 public class Person { private Integer id; private String name; public static void main(String[] args) { Person p1 = new Person(); Person p2 = new Person(1, "張益沒有達"); } }
@Accessors()
import lombok.Data; import lombok.experimental.Accessors; @Data @Accessors(chain = true) // 默認為false public class Person { private Integer id; private String name; public static void main(String[] args) { Person person = new Person(); person.setId(1).setName("張益沒有達"); System.out.println(person); } }
@Setter 和 @Getter
import lombok.Setter; @Setter public class Person { private Integer id; private String name; }
import lombok.Getter; @Getter public class Person { private Integer id; private String name; }
@NonNull
@Cleanup(不推薦使用)
其他的注解同理,可以自己下去測試!
IDEA中pom依賴導入
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.8</version> <scope>provided</scope> </dependency>