概述
在寫POJO代碼時,經常需要生成getter和setter方法,以及hashcode、tostring和equals等代碼。感覺甚是麻煩,雖然有Eclipse或者intellij IDEA快捷鍵能夠自動生成些代碼;代碼裡面充斥着這種沒有意義的片段,看着都覺得煩;另一方面,單元測試時,若是考察行覆寫率,這種POJO代碼勢必會增加分母的大小,降低代碼覆寫率。
偶然中,得知Lombok Project,通過使用注解,可以消除備援的java代碼。
官網介紹:
Project Lombok makes java a spicier language by adding ‘handlers’ that know how to build and compile simple, boilerplate-free, not-quite-java code.
如何使用
引入依賴即可:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
Gradle項目的引入方式類似:
provided group: 'org.projectlombok', name: 'lombok', version: '1.16.18'
在spring boot項目中,引入Lombok時沒有必要指定
<version>和<scope>
,spring boot已經自動配置好。
如何安裝
在IDEA中使用Lombok需要先安裝插件,快捷鍵 Ctrl + Alt + S,然後選擇 plugins——> browse repositories,搜尋 Lombok,install即可,安裝需要重新開機。
其次,Lombok是在編譯時期,自動生成所需方法的,是以需要開啟annotation processing;依舊是剛才的快捷鍵,可以搜尋"annotation processing",然後選擇"Enable annotation processing"。
注解
不完全統計:
@NonNull:辨別對象是否為空,為空則抛出異常
@Getter/@Setter:自動生成Getter/Setter方法
@ToString:覆寫tostring方法,可以添加限制條件
@EqualsAndHashCode:覆寫equal和hashCode方法,生成方法時隻會使用類中的非靜态和非transient成員變量
@Data:@Getter/@Setter, @ToString, @EqualAndHashCode,@NoArgsConstructor等組合
@Cleanup:用在變量前面,可以保證此變量代表的資源會被自動關閉,預設是調用資源的close()方法,如果該資源有其它關閉方法,可使用@Cleanup("methodName")來指定要調用的方法
@NoArgsConstructor/@RequiredArgsConstructor /@AllArgsConstructor:第二個注解使用類中所有帶有@NonNull注解的或帶有final修飾的非靜态成員變量生成對應的構造方法,
@Log:類注解,可以省去從日志工廠生成日志對象這一步,直接進行日志記錄,具體注解根據日志工具的不同而不同,同時,可以在注解中使用topic來指定生成log對象時的類名
@Builder:關于build模式,可以參考《Effective java》這本書
@Value
@SneakyThrows
@Synchronized
@Accessors(chain = true),在生成 setter 方法時不傳回 void,而是傳回 this,目前類;
執行個體代碼
@Getter & @Setter & @ToString
public class Person {
private boolean employed;
private String name;
Person() {
}
public String say() {
return this.getName();
}
public String toString() {
return "Person(employed=" + this.isEmployed() + ", name=" + this.getName() + ")";
}
public boolean isEmployed() {
return this.employed;
}
public void setEmployed(boolean employed) {
this.employed = employed;
}
public String getName() {
return this.name;
}
protected void setName(String name) {
this.name = name;
}
}
如果使用Lombok提供的注解:
@ToString
class Person {
@Getter
@Setter
private boolean employed;
@Getter
@Setter(AccessLevel.PROTECTED)
private String name;
public String say() {
return getName();
}
}
@Builder
這次先看看使用注解之前的java code:
@Builder
public class BuilderExample {
private String name;
private int age;
}
再看看其經過編譯之後的java code:
import java.beans.ConstructorProperties;
public class BuilderExample {
private String name;
private int age;
@ConstructorProperties({"name", "age"})
BuilderExample(String name, int age) {
this.name = name;
this.age = age;
}
public static BuilderExample.BuilderExampleBuilder builder() {
return new BuilderExample.BuilderExampleBuilder();
}
public static class BuilderExampleBuilder {
private String name;
private int age;
BuilderExampleBuilder() {
}
public BuilderExample.BuilderExampleBuilder name(String name) {
this.name = name;
return this;
}
public BuilderExample.BuilderExampleBuilder age(int age) {
this.age = age;
return this;
}
public BuilderExample build() {
return new BuilderExample(this.name, this.age);
}
public String toString() {
return "BuilderExample.BuilderExampleBuilder(name=" + this.name + ", age=" + this.age + ")";
}
}
}
使用:
UserVO user = UserVO.builder().id(3).username("chifanshuijiao").build();
注意:需要額外增加兩個注解:
@NoArgsConstructor
@AllArgsConstructor
@Data
先看使用注解的類:
@Data
public class User {
private Integer id;
private String password;
}
編譯之後的類長這樣:
public class User {
private Integer id;
private String password;
public User() {
}
public Integer getId() {
return this.id;
}
public String getPassword() {
return this.password;
}
public void setId(Integer id) {
this.id = id;
}
public void setPassword(String password) {
this.password = password;
}
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (!(o instanceof User)) {
return false;
} else {
User other = (User)o;
if (!other.canEqual(this)) {
return false;
} else {
Object this$id = this.getId();
Object other$id = other.getId();
if (this$id == null) {
if (other$id != null) {
return false;
}
} else if (!this$id.equals(other$id)) {
return false;
}
Object this$password = this.getPassword();
Object other$password = other.getPassword();
if (this$password == null) {
if (other$password != null) {
return false;
}
} else if (!this$password.equals(other$password)) {
return false;
}
return true;
}
}
}
protected boolean canEqual(Object other) {
return other instanceof User;
}
public int hashCode() {
int PRIME = true;
int result = 1;
Object $id = this.getId();
int result = result * 59 + ($id == null ? 43 : $id.hashCode());
Object $password = this.getPassword();
result = result * 59 + ($password == null ? 43 : $password.hashCode());
return result;
}
public String toString() {
return "User(id=" + this.getId() + ", password=" + this.getPassword() + ")";
}
}
更多注解的用法,請參考官網。
lombok.config
lombok.config
config官方文檔 檢視目前版本的lombok所支援的配置項使用指令工具config
java -jar lombok.jar config -g --verbose
cofing工具檢視某個配置檔案所有的配置項
java -jar lombok.jar config lombok.config
示例:
# Tell the configuration system it should stop looking for other configuration files (default: false).
# 指明lombok的根目錄為目前配置檔案所在目錄
# 配置檔案是分層的,原則是接近源檔案的配置設定優先。在根目錄的子目錄中可以建立lombok.config配置檔案,來覆寫根目錄的配置檔案(隻覆寫配置項相同的配置,其他繼承根目錄的配置,若沒有顯式配置則使用預設值)
config.stopBubbling = true
# Generate setters that return 'this' instead of 'void' (default: false).
lombok.accessors.chain = true
# 使用不帶字首的簡單方式生成setter和getter,即使用原始的字段名稱省略get和set字首
lombok.accessors.fluent=true
# 跳過添加一個@java.bean.ConstructorProperties生成的構造器
lombok.anyConstructor.suppressConstructorProperties=true
# Don't call the getters but use the fields directly in the generated toString method (default = false).
lombok.toString.doNotUseGetters = true
# When generating toString for classes that extend something (other than Object), either automatically take into account superclass implementation (call), or don't (skip), or warn and don't (warn). (default = warn).
lombok.toString.callSuper = CALL
要求:lombok 1.14+
原理
采用JSR269所提出的插入式注解處理(Pluggable Annotation Processing),并結合動态代碼生成技術所開發的。如下圖所示:
圖示為 javac 的編譯過程,java檔案首先通過進行解析建構出一個AST,然後執行注解處理,最後經過分析優化生成二進制的.class檔案。
Annotation Processing 是在解析和生成之間的一個步驟。
上圖是 Lombok 處理流程,在Javac 解析成抽象文法樹之後(AST),Lombok 根據自己的注解處理器,動态的修改 AST,增加新的節點(所謂代碼),最終通過分析和生成位元組碼。
其他注解
Wither
val
無憂的final本地變量
NonNull
Cleanup
自動的資源管理:安全調用你的close() 方法,無需任何麻煩。