前言
本文主要介紹的是Java插件Lombok。
Lombok簡介
Lombok
項目是一個Java庫,它會自動插入您的編輯器和構建工具中,爲您的Java增光添彩。
Lombok
是一款Java開發插件,使得Java開發者通過其定義的一些註解來消除業務工程中冗長和繁瑣的代碼, 尤其對於簡單的Java模型對象(POJO
)。在開發環境中使用Lombok
插件後,Java開發人員可以節省出重複構建,諸如hashCode
和equals這樣的方法以及各種業務對象模型的accessor
和ToString
等方法的大量時間。對於這些方法,它能夠在編譯源代碼期間自動幫我們生成這些方法,並沒有如反射那樣降低程序的性能 。官網鏈接
簡而言之:Lombok
能以簡單的註解形式來簡化Java
代碼,提高開發人員的開發效率。
Lombok使用
使用Lombok需要的開發環境Java+Maven+IDEA或者Eclipse(安裝Lombok Plugin)
添加maven依賴
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
<scope>provided</scope>
</dependency>
安裝插件
使用Lombok還需要插件的配合,下面展示的是如何在IDEA中安裝Lombok插件
打開idea的設置,點擊Plugins,點擊Browse repositories,在彈出的窗口中搜索lombok,然後安裝即可。
解決編譯時出錯問題
編譯時出錯,可能是沒有enable註解處理器。Annotation Processors > Enable annotation processing
。設置完成之後程序正常運行。
示例
下面舉兩個例子,看看使用lombok和不適用的區別
創建一個Role類
不使用lombok
public class Role {
private Integer roleId;
private String roleName;
public Integer getRoleId() {
return roleId;
}
public String getRoleName() {
return roleName;
}
public void setRoleId(Integer roleId) {
this.roleId = roleId;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
@Override
public String toString() {
return "Role{" +
"roleId=" + roleId +
", roleName='" + roleName + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Role role = (Role) o;
return Objects.equals(roleId, role.roleId) &&
Objects.equals(roleName, role.roleName);
}
@Override
public int hashCode() {
return Objects.hash(roleId, roleName);
}
}
使用Lombok
@Data
public class Role implements Serializable {
private static final long serialVersionUID = -8054600833969507380L;
private Integer roleId;
private String roleName;
}
編譯源文件,然後反編譯class文件,如下圖。說明說明@Data註解在類上,會爲類的所有屬性自動生成setter/getter、equals、canEqual、hashCode、toString方法,如爲final屬性,則不會爲該屬性生成setter方法。
public class Role
implements Serializable {
private static final long serialVersionUID = -8054600833969507380L;
private Integer roleId;
private String roleName;
public void setRoleId(Integer roleId) {
this.roleId = roleId;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (!(o instanceof Role)) {
return false;
}
Role other = (Role) o;
if (!other.canEqual(this)) {
return false;
}
Object this$roleId = getRoleId(), other$roleId = other.getRoleId();
if ((this$roleId == null) ? (other$roleId != null) : !this$roleId.equals(other$roleId)) {
return false;
}
Object this$roleName = getRoleName(), other$roleName = other.getRoleName();
return !((this$roleName == null) ? (other$roleName != null)
: !this$roleName.equals(other$roleName));
}
protected boolean canEqual(Object other) {
return other instanceof Role;
}
public int hashCode() {
int PRIME = 59;
result = 1;
Object $roleId = getRoleId();
result = result * 59 + (($roleId == null) ? 43 : $roleId.hashCode());
Object $roleName = getRoleName();
return result * 59 + (($roleName == null) ? 43 : $roleName.hashCode());
}
public String toString() {
return "Role(roleId=" + getRoleId() + ", roleName=" + getRoleName() + ")";
}
public Integer getRoleId() {
return this.roleId;
}
public String getRoleName() {
return this.roleName;
}
自動化日誌變量
@Slf4j
@RestController
@RequestMapping("/user")
@GetMapping("/findAllOfPage")
public Object findAllOfPage(
@RequestParam(name = "pageNum", required = false, defaultValue = "1") int pageNum,
@RequestParam(name = "pageSize", required = false, defaultValue = "10") int pageSize) {
if (log.isInfoEnabled()) {
log.debug("查詢失敗");
}
return userService.findAll(pageNum, pageSize);
}
通過反編譯可以看到@Slf4h註解生成了log日誌變量, 無需去聲明一個log就可以在類中使用log記錄日誌。
@RestController
@RequestMapping({"/user"})
public class UserController
{
private static final Logger log = LoggerFactory.getLogger(UserController.class);
@GetMapping({"/findAllOfPage"})
public Object findAllOfPage(@RequestParam(name = "pageNum", required = false, defaultValue = "1") int pageNum, @RequestParam(name = "pageSize", required = false, defaultValue = "10") int pageSize) {
if (log.isInfoEnabled()) {
log.debug("��������");
}
return this.userService.findAll(pageNum, pageSize);
}
Lombok常用註解
- @Setter 註解在類或者字段上,註解在類時爲所用字段生成setter方法,註解在字段上時只爲該字段生成setter方法。
- @Getter 使用方法類同@Setter,區別在於生成的是getter方法。
- @ToString 註解在類,添加toString方法
- @EqualsAndHashCode 註解在類,生成hashCode和equals方法
- @NoArgsConstructor 註解在類,生成無參的構造方法
- @RequiredArgsConstructor 註解在類,爲類中需要特殊處理的字段生成構造方法, 比如final和被@NonNull註解的字段。
- @AllArgsConstructor 註解在類,生成包含所有字段的構造方法
- @Data 註解在類, 生成setter/getter、equals、canEqual、hashCode、toString方法,如爲final屬性,則不會爲該屬性生成setter方法。
- @Slf4 註解在類, 生成log變量,嚴格意義來說是常量。
private static final Logger log = LoggerFactory.getLogger(UserController.class)
- Val 可以將變量申明是final類型
- @Log 可以生成不同類型的log日誌對象,實例名都是log方便用於打印日誌
- @Builder 可以用在類、構造器、方法上, 註解提供了一種比較推崇的構建值對象的方式。
- @Synchronized 註解類似Java中的Synchronized 關鍵字,但是可以隱藏同步鎖。
- @NonNull 註解能夠爲方法或構造函數的參數提供非空檢查
- @Cleanup 註解能夠自動釋放資源
Lombok的自定義註解原理
Lombok這款插件依靠可插件化的Java自定義註解處理API( JSR 269: Pluggable Annotation Processing API )來實現在Javac編譯階段你用"Annotation Processor"對自定義的註解進行預處理後生成真正在JVM上面執行的clss文件。原理圖如下:
從上面的原理圖可以看出Annotation Processing是編譯器在解析Java源代碼和生成Class文件之間的一個步驟。其中Lombok插件具體的執行流程如下:
從上面的Lombok執行的流程圖中可以看出,在JavaC解析成AST抽象語法樹之後,Lombok根據自己編寫的註解處理器,動態地修改AST,增加新的節點(即Lombok自定義註解所需要生成的代碼),最終通過分析生成JVM可執行的字節碼Class文件。 使用Annotation Processing自定義註解是在編譯階段進行修改,而JDK的反射技術是在運行時動態修改,兩者相比,反射雖然更加靈活一些但是帶來的性能損耗更加大。
Lombok本質上就是實現了“JSP 269 API”的程序。在使用javac的過程中,它產生作用的具體流程如下:
- javac對源代碼進行分析,生成了一棵抽象語法樹(AST)
- 運行過程中調用實現了“JSR 269 API”的Lombok程序
- Lombok就對第一步驟得到的AST進行處理,找到@Data註解所在類對應的語法樹(AST),然後修改該語法樹(AST),增加getter和setter方法定義的相應樹節點
- 使用修改後的抽象語法樹(AST)生成字節碼文件,即給class增加新的節點(代碼塊)
Lombok的優缺點
優點:
- 能通過註解的形式自動生成構造器、getter/setter、equals、hashcode、toString等方法,提高了一定的開發效率
- 讓代碼變得簡潔,不用過多的去關注相應的方法
- 屬性做修改時,也簡化了維護爲這些屬性所生成的getter/setter方法等
缺點:
- 不支持多種參數構造重載
- 雖然省去了手動創建getter/setter方法的麻煩,但大大降低了源代碼的可讀性和完整性,降低了閱讀源代碼的舒適度