今天為大家推薦一個(gè)java的開(kāi)發(fā)神器lombok,可以幫助大家節(jié)省開(kāi)發(fā)時(shí)間,提升工(mo)作(yu)效(shi)率(jian)。
一、基本介紹
Lombok是一款Java開(kāi)發(fā)插件,可以通過(guò)它定義的注解來(lái)精簡(jiǎn)冗長(zhǎng)和繁瑣的代碼,主要針對(duì)簡(jiǎn)單的Java模型對(duì)象(POJO)。
好處就顯而易見(jiàn)了,可以節(jié)省大量重復(fù)工作,特別是當(dāng)POJO類的屬性增減時(shí),需要重復(fù)修改的Getter/Setter、構(gòu)造器方法、equals方法和toString方法等。
而且Lombok針對(duì)這些內(nèi)容的處理是在編譯期,而不是通過(guò)反射機(jī)制,這樣的好處是并不會(huì)降低系統(tǒng)的性能。
目前Lombok已經(jīng)支持JDK14和JDK13的一些特性
二、IDE安裝Lombok開(kāi)發(fā)工具
Lombok的官網(wǎng)中詳細(xì)的介紹了各種IDE安裝Lombok的方法,目前已經(jīng)指出IDEA、Eclipse、Netbeans、Myeclipse、Spring boot Suite、Visual Studio Code等工具,本文以IDEA為例,介紹安裝的方法,其他IDE的安裝方法按照官網(wǎng)的方式進(jìn)行安裝。
2.1 在線安裝
點(diǎn)擊File -> Settings -> Plugins
-
在marketplace中搜索Lombok
Lombok -
點(diǎn)擊install
安裝完成 -
Restart IntelliJ IDEA
重新IDEA之后
2.2 離線安裝
在一些管理比較嚴(yán)格的公司里,開(kāi)發(fā)環(huán)境不允許連接外網(wǎng),離線安裝的方法主要方便這些苦難的兄弟
1.進(jìn)入jetbrains官網(wǎng),找到Lombook插件
2.根據(jù)自己IDEA版本選擇對(duì)應(yīng)的版本

-
點(diǎn)擊File -> Settings -> Plugins,選擇install plugins from disk
image.png
4.選擇下載的插件,后續(xù)與在線安裝過(guò)程一致
注意:選擇下載插件時(shí),一定要與IDEA的版本一致,如不一致,會(huì)提示不可用,或者安裝失敗
三、引入依賴
Lombok的官網(wǎng)中分別介紹了maven、gradle、ant和Kobalt四種構(gòu)建工具如何引入Lombok,本文以maven為例進(jìn)行簡(jiǎn)單的介紹,其他構(gòu)建工具可以參考官網(wǎng)的文檔
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
</dependencies>
其中,1.18.12版本為當(dāng)前最新版本,<scope>provided</scope> 表示編譯時(shí)不會(huì)將其打包war/jar包中。
四、注解簡(jiǎn)介
Lombok的所有注解可以通過(guò)官網(wǎng)Lombok features查看
1. @Getter/@Setter
這兩個(gè)注意應(yīng)該是使用lombok中最常用的的兩個(gè)注解,一個(gè)屬性foo,通過(guò)@Getter注解生成的get方法的方法名為getFoo(),如果該屬性為boolean,則方法名為isFoo();set方法名為setFoo,返回值為void。
@Setter/@Getter生成的方法為public,如果想要修改方法的訪問(wèn)修飾符,可以使用AccessLevel進(jìn)行限制,AccessLevel支持所有的訪問(wèn)修飾符(PUBLIC、PROTECTED、PACKAGE、PRIVATE)
@Setter(AccessLevel.PROTECTED) private String name;
官網(wǎng)示例:
Lombok版本
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
public class GetterSetterExample {
/**
* Age of the person. Water is wet.
*
* @param age New value for this person's age. Sky is blue.
* @return The current value of this person's age. Circles are round.
*/
@Getter @Setter private int age = 10;
/**
* Name of the person.
* -- SETTER --
* Changes the name of this person.
*
* @param name The new value.
*/
@Setter(AccessLevel.PROTECTED) private String name;
@Override public String toString() {
return String.format("%s (age: %d)", name, age);
}
}
不使用Lombok的程序
public class GetterSetterExample {
/**
* Age of the person. Water is wet.
*/
private int age = 10;
/**
* Name of the person.
*/
private String name;
@Override public String toString() {
return String.format("%s (age: %d)", name, age);
}
/**
* Age of the person. Water is wet.
*
* @return The current value of this person's age. Circles are round.
*/
public int getAge() {
return age;
}
/**
* Age of the person. Water is wet.
*
* @param age New value for this person's age. Sky is blue.
*/
public void setAge(int age) {
this.age = age;
}
/**
* Changes the name of this person.
*
* @param name The new value.
*/
protected void setName(String name) {
this.name = name;
}
}
示例中的@Setter和@Getter注解分別放到了具體的屬性前,在開(kāi)發(fā)過(guò)程中,通常直接放到類的定義前,這樣表示該類中的所有屬性都要生成set/get方法
2.@NonNull
作用于屬性上,提供關(guān)于此參數(shù)的非空檢查,如果參數(shù)為空,則拋出空指針異常。
非空檢查的代碼示例如下:
if (param == null)
throw new NullPointerException("param is marked @NonNull but is null")
官方示例:
Lombok版本
public class NonNullExample extends Something {
private String name;
public NonNullExample(@NonNull Person person) {
super("Hello");
this.name = person.getName();
}
}
不帶Lombok版本
public class NonNullExample extends Something {
private String name;
public NonNullExample(Person person) {
super("Hello");
if (person == null) {
throw new NullPointerException("person is marked @NonNull but is null");
}
this.name = person.getName();
}
}
注意:官網(wǎng)中Vanilla Java版本中在Person person前增加了@NonNull 注解,應(yīng)該是編寫(xiě)錯(cuò)誤,此處特意進(jìn)行說(shuō)明
3.@NoArgsConstructor/@RequiredArgsConstructor/@AllArgsConstructor
這三個(gè)注解主要是生成構(gòu)造函數(shù)的標(biāo)簽,其中:
@NoArgsConstructor表示生成一個(gè)不帶參數(shù)的構(gòu)造方法;
@RequiredArgsConstructor表示生成一些特定屬性的構(gòu)造方法,特定參數(shù)包括final的字段和標(biāo)記@NonNull的字段;使用該標(biāo)簽生成的構(gòu)造方法的訪問(wèn)修飾符為private,可以增加staticName屬性值,該屬性默認(rèn)為“”,增加該屬性后,會(huì)默認(rèn)生成一個(gè)名為staticName值的構(gòu)造方法,其訪問(wèn)修飾符為public的靜態(tài)方法,參數(shù)與@RequiredArgsConstructor參數(shù)一致
@AllArgsConstructor表示生成所有屬性的構(gòu)造方法;
官方示例:
Lombok版本
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.AllArgsConstructor;
import lombok.NonNull;
@RequiredArgsConstructor(staticName = "of")
@AllArgsConstructor(access = AccessLevel.PROTECTED)
@NoArgsConstructor
public class ConstructorExample<T> {
private int x, y;
@NonNull private T description;
}
不帶Lombok版本
public class ConstructorExample<T> {
private int x, y;
@NonNull private T description;
}
// @NoArgsConstructor對(duì)應(yīng)的構(gòu)造方法
private ConstructorExample(T description) {
}
// @RequiredArgsConstructor對(duì)應(yīng)的構(gòu)造方法
private ConstructorExample(T description) {
if (description == null) throw new NullPointerException("description");
this.description = description;
}
// 增加staticName后生成的構(gòu)造方法
public static <T> ConstructorExample<T> of(T description) {
return new ConstructorExample<T>(description);
}
// @AllArgsConstructor對(duì)應(yīng)的構(gòu)造方法
@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;
}
官方給出的示例讓人看起來(lái)有點(diǎn)摸不著頭腦,為了方便清楚的明白這三個(gè)標(biāo)簽,將官方的示例進(jìn)行調(diào)整,效果可能會(huì)更好一點(diǎn)。
查看使用三個(gè)注解生成的方法,與不使用Lombok的構(gòu)造方法完全一致

4. @ToString
@ToString自動(dòng)生成一個(gè)toString()方法,默認(rèn)情況是所有的字段都在進(jìn)行字符串輸出,當(dāng)然,可以使用@ToString.Exclude的方式排除一些字段不顯示輸出。
5.@Data
@Data注解集成了@ToString、@EqualsAndHashCode、@Getter / @Setter和@RequiredArgsConstructor
6.@EqualsAndHashCode
@EqualsAndHashCode生成hashCode()、canEqual和equals()方法
7.val/var
使用val作為局部變量聲明的類型,而不是實(shí)際寫(xiě)入類型。 執(zhí)行此操作時(shí),將從初始化表達(dá)式推斷出類型。
var與val幾乎一樣,只是變量前沒(méi)有final的定義
我理解var和val與javascript中的左右?guī)缀跸嗤趯?duì)變量賦值之后在進(jìn)行賦值,這兩個(gè)屬性不是注解,是一個(gè)類,實(shí)際場(chǎng)景中可能的用途不是太多
官方示例:
Lombok版本
import java.util.ArrayList;
import java.util.HashMap;
import 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();
}
}
不帶Lombok版本
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
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();
}
}
其他的屬性可以參考官網(wǎng)中穩(wěn)定版本的特性列表,不在額外敘述
五、關(guān)于Lombok的一些爭(zhēng)議
之前在網(wǎng)上有人發(fā)布Lombok是讓你代碼處于“亞健康”狀態(tài)的真正元兇,提出Lombok存在的四個(gè)問(wèn)題:
* JDK8升級(jí)到JDK11后,Lombok不能使用
* 脅迫使用:當(dāng)你的源代碼中使用了Lombok,恰好你的代碼又被其他的人所使用,那么依賴你代碼的人,也必須安裝Lombok插件
* 可讀性差:Lombok隱藏了JavaBean封裝的細(xì)節(jié),所有JavaBean中的方法你只能想象他們長(zhǎng)什么樣子,你并不能看見(jiàn)
* 代碼耦合度增加:當(dāng)你使用Lombok來(lái)編寫(xiě)某一個(gè)模塊的代碼后,其余依賴此模塊的其他代碼都需要引入Lombok依賴,同時(shí)還需要在IDE中安裝Lombok的插件,這是一種入侵式的耦合
關(guān)于第一個(gè)問(wèn)題,其實(shí)是Lombok的版本原因,在項(xiàng)目中使用了相對(duì)較低的版本,會(huì)存在升級(jí)JDK后不能使用的問(wèn)題,升級(jí)到最新的Lombok就可,那會(huì)不會(huì)有有問(wèn)題呢?呵呵呵,JDK都升級(jí)了還怕插件升級(jí)有問(wèn)題嗎?
關(guān)于第二點(diǎn),的確是不可避免的事實(shí),既然都是使用到代碼級(jí)別,那應(yīng)該是一個(gè)團(tuán)隊(duì)或者一個(gè)公司級(jí)別,換一個(gè)思路思考,那團(tuán)隊(duì)或者公司要求使用相同的架構(gòu)和框架是不是也很合理?
關(guān)于第三個(gè)問(wèn)題,實(shí)際上可以適當(dāng)?shù)倪x擇Lombok的注解,對(duì)于不好用的注解可以選擇不使用。在我看來(lái),@Getter和@Setter就可以解決我大量的工作
關(guān)于第四個(gè)問(wèn)題,實(shí)際上可以使用maven的配置就可以解決相關(guān)問(wèn)題,可以增加<optional>true</optional>,這樣依賴可以不傳給引用的項(xiàng)目。
參考文獻(xiàn)
1.Project Lombok
2.Java開(kāi)發(fā)神器Lombok使用詳解
3.Lombok是讓你代碼處于“亞健康”狀態(tài)的真正元兇



