Java插件Lombok的簡介以及Lombok的使用

前言

本文主要介紹的是Java插件Lombok。

Lombok簡介

Lombok項目是一個Java庫,它會自動插入您的編輯器和構建工具中,爲您的Java增光添彩。

Lombok是一款Java開發插件,使得Java開發者通過其定義的一些註解來消除業務工程中冗長和繁瑣的代碼, 尤其對於簡單的Java模型對象(POJO)。在開發環境中使用Lombok插件後,Java開發人員可以節省出重複構建,諸如hashCode和equals這樣的方法以及各種業務對象模型的accessorToString等方法的大量時間。對於這些方法,它能夠在編譯源代碼期間自動幫我們生成這些方法,並沒有如反射那樣降低程序的性能 。官網鏈接

簡而言之: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文件。原理圖如下:

img

從上面的原理圖可以看出Annotation Processing是編譯器在解析Java源代碼和生成Class文件之間的一個步驟。其中Lombok插件具體的執行流程如下:

img

從上面的Lombok執行的流程圖中可以看出,在JavaC解析成AST抽象語法樹之後,Lombok根據自己編寫的註解處理器,動態地修改AST,增加新的節點(即Lombok自定義註解所需要生成的代碼),最終通過分析生成JVM可執行的字節碼Class文件。 使用Annotation Processing自定義註解是在編譯階段進行修改,而JDK的反射技術是在運行時動態修改,兩者相比,反射雖然更加靈活一些但是帶來的性能損耗更加大。

Lombok本質上就是實現了“JSP 269 API”的程序。在使用javac的過程中,它產生作用的具體流程如下:

  1. javac對源代碼進行分析,生成了一棵抽象語法樹(AST)
  2. 運行過程中調用實現了“JSR 269 API”的Lombok程序
  3. Lombok就對第一步驟得到的AST進行處理,找到@Data註解所在類對應的語法樹(AST),然後修改該語法樹(AST),增加getter和setter方法定義的相應樹節點
  4. 使用修改後的抽象語法樹(AST)生成字節碼文件,即給class增加新的節點(代碼塊)

Lombok的優缺點

優點:

  • 能通過註解的形式自動生成構造器、getter/setter、equals、hashcode、toString等方法,提高了一定的開發效率
  • 讓代碼變得簡潔,不用過多的去關注相應的方法
  • 屬性做修改時,也簡化了維護爲這些屬性所生成的getter/setter方法等

缺點

  • 不支持多種參數構造重載
  • 雖然省去了手動創建getter/setter方法的麻煩,但大大降低了源代碼的可讀性和完整性,降低了閱讀源代碼的舒適度
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章