Lombok 介紹及註解的使用

一、Lombok簡介

    Lombok是一個可以通過簡單的註解形式來幫助我們簡化消除一些必須有但顯得很臃腫的Java代碼的工具,通過使用對應的註解,可以在編譯源碼的時候生成對應的方法。

 

二、爲何要使用Lombok

    我們在開發過程中,通常都會定義大量的JavaBean,然後通過IDE去生成其屬性的構造器、hashcode、toString、getter、setter、equals方法,當要增加屬性或者對某個屬性進行改變時,比如命名、類型等,都需要重新去生成上面提到的這些方法。這樣重複的勞動沒有任何意義,Lombok裏面的註解可以輕鬆解決這些問題。

  • 簡化冗餘的JavaBean代碼,使得實體文件很簡潔。

  • 大大提高JavaBean中方法的執行效率,省去重複的步驟

    這裏簡單說下Lombok實現的原理:主要是通過抽象語法樹(AST),在編譯處理後,匹配到有其註解的類,那麼註解編譯器就會自動去匹配項目中的註解對應到在Lombok語法樹中的註解文件,並經過自動編譯匹配來生成對應類中的getter或者setter方法,達到簡化代碼的目的。

      利用此原理,也可自行編寫一些工作中一些經常使用到的,比如實體類轉Map對象,map對象轉實體類,原本使用Beanutils或者cglib的BeanCopier實現轉換,前者使用的是反射的機制,所以性能相對較差,後者是使用修改字節碼技術,性能在未使用Converter時基本等同於set和get方法。但說白了還是麻煩,畢竟還需要緩存對象等做到複用等。而使用Lombok的形式的話,一切都是自動的,性能基本是沒有損失的。

 

三、IDEA中安裝Lombok插件

1、在頂部工具欄中找到File → Settings

 

2、找到Plugins,點擊 “Browse repositories”按鈕

 

3、在搜索框輸入Lombok,選擇Lombok插件,點擊 “Install”按鈕進行安裝,然後點擊 “Close”按鈕,最後重啓IDEA即可生效

 

四、添加Lombok依賴

<dependency>
     <groupId>org.projectlombok</groupId>
     <artifactId>lombok</artifactId>
     <version>1.16.20</version>
     <scope>provided</scope>
</dependency>

 

五、Lombok的常用註解

  • @Getter/@Setter

  • @ToString

  • @NonNull

  • @NoArgsConstructor/@RequiredArgsConstructor /@AllArgsConstructor

  • @EqualsAndHashCode

  • @Data/@Value

  • @Cleanup

  • @Budilder

  • @SneakyThrows

  • @Synchronized

  • @Log

 

六、Lombok註解的介紹與使用

1、@Getter/@Setter

        該註解使用在類或者成員變量上,如果使用在上,可以爲類中所有非靜態成員變量生成對應的get 和 set方法。如果使用在成員變量上,只能爲該成員變量生成對應的get 和 set方法。同時還能夠爲生成的get 和 set方法指定訪問修飾符,如果不指定默認爲public

1.1、使用在類上(不指定訪問修飾符,默認爲public)

package com.lombok;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class User {

    private String username;
    private String password;

}

實際上的效果相當於:

package com.lombok;

public class User {

    private String username;
    private String password;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

}

 

1.2、使用在成員變量上,同時指定訪問修飾符

package com.lombok;

import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;

public class User {

    @Getter
    @Setter(AccessLevel.PROTECTED)
    private String username;

    @Getter(AccessLevel.PUBLIC)
    @Setter(AccessLevel.PRIVATE)
    private String password;

}

實際上的效果相當於:

package com.lombok;

public class User {

    private String username;
    private String password;

    public String getUsername() {
        return username;
    }

    protected void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    private void setPassword(String password) {
        this.password = password;
    }
}

 

 

2、@ToString

       該註解使用在上,用來爲類生成toString()方法,默認包含所有成員變量,同時可以使用exclude 或者 of用來從生成的toString()方法中排出 或者 包含某些指定的成員變量

2.1、使用eclude來排除指定的成員變量

package com.lombok;

import lombok.ToString;

@ToString(exclude = {"username","password"})
public class User {

    private String username;
    private String password;
    private Integer age;

}

實際上的效果相當於:

package com.lombok;

public class User {

    private String username;
    private String password;
    private Integer age;

    @Override
    public String toString() {
        return "User(age=" + this.age + ")";
    }
}

 

2.2、使用of來包含指定的成員變量

package com.lombok;

import lombok.ToString;

@ToString(of = {"username","password"})
public class User {

    private String username;
    private String password;
    private Integer age;

}

實際上的效果相當於:

package com.lombok;

public class User {

    private String username;
    private String password;
    private Integer age;

    @Override
    public String toString() {
        return "User(username=" + this.username + ", password=" + this.password + ")";
    }
}

 

 

3、@NonNull

       該註解使用在成員變量上 或者 方法(包括:構造方法)的參數前面,會自動產生一個關於成員變量 或者 參數的非空檢查,如果參數爲空,則拋出一個空指針異常

package com.lombok;

import lombok.Getter;
import lombok.NonNull;
import lombok.Setter;

public class User {

    @Getter
    @Setter
    @NonNull
    private String username;

}

實際上的效果相當於:

package com.lombok;

import lombok.NonNull;

public class User {

    @NonNull
    private String username;

    public void setUsername(@NonNull String username) {
        if (username == null){
            throw  new NullPointerException("username");
        }else{
            this.username = username;
        }
    }

    @NonNull
    public String getUsername() {
        return username;
    }
}

 

 

4、@NoArgsConstructor/@RequiredArgsConstructor /@AllArgsConstructor

這三個註解都是用在

     @NoArgsConstructor:生成一個無參的構造方法

     @RequiredArgsConstructor:使用類中所有帶有@NonNull註解的 或者 帶有final修飾的成員變量生成對應的構造方法

     @AllArgsConstructor:生成一個包含所以參數的構造方法

package com.lombok;

import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;

@NoArgsConstructor
@AllArgsConstructor
@RequiredArgsConstructor
public class User {

    @NonNull
    private String username;
    private String password;
    private Integer age;
}

實際上的效果相當於:

package com.lombok;

import lombok.NonNull;

public class User {

    @NonNull
    private String username;
    private String password;
    private Integer age;

    public User() {
    }

    public User(String username, String password, Integer age) {
        this.username = username;
        this.password = password;
        this.age = age;
    }

    public User(String username) {
        this.username = username;
    }
}

 

5、@EqualsAndHashCode

        該註解使用在上,爲類生成equals、hashCode和canEqual方法 (用於判斷某個對象是否是當前類的實例),生成方法時只會使用類中的非靜態和非transient成員變量,同時@EqualsAndHashCode 也可以使用exclude 或者 of用來排除 或者 包含某些指定的字段做比較

package com.lombok;

import lombok.EqualsAndHashCode;

@EqualsAndHashCode
public class User {

    private String username;
    private String password;
    private static Integer age;
    
}

實際上的效果相當於:

package com.lombok;

public class User {

    private String username;
    private String password;
    private static Integer age;
    
    @Override
    public boolean equals(final Object o) {
        if (o == this){
            return true;
        }
        if (!(o instanceof User)) {
            return false;
        }
        final User other = (User) o;
        if (!other.canEqual((Object) this)) {
            return false;
        }
        final Object this$username = this.username;
        final Object other$username = other.username;
        if (this$username == null ? other$username != null : !this$username.equals(other$username)) {
            return false;
        }
        final Object this$password = this.password;
        final Object other$password = other.password;
        if (this$password == null ? other$password != null : !this$password.equals(other$password)) {
            return false;
        }
        return true;
    }

    protected boolean canEqual(final Object other) {
        return other instanceof User;
    }

    @Override
    public int hashCode() {
        final int PRIME = 59;
        int result = 1;
        final Object $username = this.username;
        result = result * PRIME + ($username == null ? 43 : $username.hashCode());
        final Object $password = this.password;
        result = result * PRIME + ($password == null ? 43 : $password.hashCode());
        return result;
    }
}

 

 

6、@Data/@Value

     @Data註解相當於@Getter/@Setter、@ToString、@EqualsAndHashCode和@RequiredArgsConstructor的綜合

     @Value註解與@Data類似,唯一的區別在於它會把所有成員變量默認定義爲private final修飾,並且不會生成set方法

 

 

7、@Cleanup

     這個註解用在變量前面,可以保證此變量代表的資源會被自動關閉,默認是調用資源的close()方法,如果該資源有其它關閉方法,可使用@Cleanup(“methodName”)來指定要調用的方法

package com.lombok;

import lombok.Cleanup;
import java.io.*;

public class MyTest {

    public static void main(String[] args) throws IOException {
        @Cleanup InputStream input = new FileInputStream(args[0]);
        @Cleanup OutputStream output = new FileOutputStream(args[1]);
        byte[] bytes = new byte[1024];
        while (true) {
            int num = input.read(bytes);
            if (num == -1) {
                break;
            }
            output.write(bytes, 0, num);
        }
    }

}

實際上的效果相當於:

package com.lombok;

import java.io.*;

public class MyTest {
    
    public static void main(String[] args) throws IOException {
        InputStream input = new FileInputStream(args[0]);
        try {
            OutputStream output = new FileOutputStream(args[1]);
            try {
                byte[] bytes = new byte[1024];
                while (true) {
                    int readNum = input.read(bytes);
                    if (readNum == -1) {
                        break;
                    }
                    output.write(bytes, 0, readNum);
                }
            } finally {
                if (output != null) {
                    output.close();
                }
            }
        } finally {
            if (input != null) {
                input.close();
            }
        }
    }

}

 

 

8、@Budilder

       該註解使用在上,可以使用Builder方式進行初始化,Builder的作用之一是爲了解決在某個類中有很多構造函數的情況,同時也省去寫很多構造函數的麻煩,在設計模式中的思想是:用一個內部類去實例化一個對象,避免一個類出現過多構造函數。

package com.lombok;

import lombok.Builder;

@Builder
public class User {
    
    private String username;
    private String password;
    private Integer age;
    
}

實際上的效果相當於: 

package com.lombok;

public class User {

    private String username;
    private String password;
    private Integer age;

    User(String username, String password, Integer age) {
        this.username = username;
        this.password = password;
        this.age = age;
    }

    public static UserBuilder builder() {
        return new UserBuilder();
    }

    public static class UserBuilder {
        private String username;
        private String password;
        private Integer age;

        UserBuilder() {
        }

        public UserBuilder username(String username) {
            this.username = username;
            return this;
        }

        public UserBuilder password(String password) {
            this.password = password;
            return this;
        }

        public UserBuilder age(Integer age) {
            this.age = age;
            return this;
        }

        public User build() {
            return new User(username, password, age);
        }

        @Override
        public String toString() {
            return "User.UserBuilder(username=" + this.username + ", password=" + this.password + ", age=" + this.age + ")";
        }
    }
}

使用Builder方式對User類進行初始化:

package com.lombok;

public class MyTest {

    public static void main(String[] args) {
        User user = User.builder().username("lemon").password("123456").age(18).build();
    }
}

 

 

9、@SneakyThrows

        這個註解用在方法上,可以將方法中的代碼用try-catch語句包裹起來,捕獲異常並在catch中用Lombok.sneakyThrow(e)把異常拋出,可以使用@SneakyThrows(Exception.class)的形式指定拋出哪種異常

package com.lombok;

import lombok.SneakyThrows;
import java.io.UnsupportedEncodingException;

public class SneakyThrowsExample implements Runnable {

    @SneakyThrows(UnsupportedEncodingException.class)
    public String utf8ToString(byte[] bytes) {
        return new String(bytes, "UTF-8");
    }

    @SneakyThrows
    public void run() {
        throw new Throwable();
    }
}

實際上的效果相當於:

package com.lombok;

import lombok.Lombok;
import java.io.UnsupportedEncodingException;

public class SneakyThrowsExample implements Runnable {

    public String utf8ToString(byte[] bytes) {
        try {
            return new String(bytes, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw Lombok.sneakyThrow(e);
        }
    }

    public void run() {
        try {
            throw new Throwable();
        } catch (Throwable t) {
            throw Lombok.sneakyThrow(t);
        }
    }
}

 

 

10、@Synchronized

        這個註解用在類方法或者實例方法上,效果和synchronized關鍵字相同,區別在於鎖對象不同,對於類方法和實例方法,synchronized關鍵字的鎖對象分別是類的class對象和this對象,這可能會導致死鎖現象。一般情況下建議鎖定一個專門用於此目的的獨立鎖,而不是允許公共對象進行鎖定。 而@Synchronized的鎖對象分別是私有靜態final對象l鎖和私有final對象鎖。

package com.lombok;

import lombok.Synchronized;

public class MyTest {

    private final Object lock = new Object();

    @Synchronized
    public static void show() {
        System.out.println("Hello World");
    }

    @Synchronized(value = "lock")
    public void good() {
        System.out.println("Today is a good day");
    }

}

實際上的效果相當於:

package com.lombok;

public class MyTest {

    private static final Object $LOCK = new Object[0];
    private final Object lock = new Object();

    public static void show() {
        synchronized ($LOCK) {
            System.out.println("Hello World");
        }
    }

    public void good() {
        synchronized (lock) {
            System.out.println("Today is a good day");
        }
    }
}

 

 

11、@Log

        這個註解用在上,可以省去從日誌工廠生成日誌對象這一步,直接進行日誌記錄,具體註解根據日誌工具的不同而不同,同時,可以在註解中可以使用topic來指定生成log對象時的類名。默認情況下,將使用@Log註解的類的類名稱作爲生成log對象時的類名

package com.lombok;

import lombok.extern.apachecommons.CommonsLog;
import lombok.extern.java.Log;
import lombok.extern.slf4j.Slf4j;

@Log
public class LogExample {

    public static void main(String... args) {
        System.out.println("Something's wrong here");
    }
}


@Slf4j
class LogExampleOther {

    public static void main(String... args) {
        System.out.println("Something else is wrong here");
    }
}


@CommonsLog(topic="CounterLog")
class LogExampleCategory {

    public static void main(String... args) {
        System.out.println("Calling the 'CounterLog' with a message");
    }
}

實際上的效果相當於:

package com.lombok;

public class LogExample {

    private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName());

    public static void main(String... args) {
        System.out.println("Something's wrong here");
    }
}


class LogExampleOther {

    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExampleOther.class);

    public static void main(String... args) {
        System.out.println("Something else is wrong here");
    }
}


class LogExampleCategory {

    private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog("CounterLog");
    
    public static void main(String... args) {
        System.out.println("Calling the 'CounterLog' with a message");
    }
}

 

@Log不同日誌註解的總結,如下所示

@CommonsLog
private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(LogExample.class);

@JBossLog
private static final org.jboss.logging.Logger log = org.jboss.logging.Logger.getLogger(LogExample.class);

@Log
private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName());

@Log4j
private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(LogExample.class);

@Log4j2
private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);

@Slf4j
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExample.class);

@XSlf4j
private static final org.slf4j.ext.XLogger log = org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);

 

 

                 

                      如果有遇到不懂或者有問題時,可以掃描下方二維碼,歡迎進羣交流與分享,希望能夠跟大家交流學習!

                                                                    

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章