1. 程式人生 > >Java開發利器Lombok

Java開發利器Lombok

  • Lombok簡介

Lombok是一種Java的實用工具,可用來幫助開發人員消除Java的冗長程式碼,尤其是對於簡單的Java物件(POJO)。它通過註釋實現這一目的。通過在開發環境中實現Lombok,開發人員可以節省構建getter/setter以及諸如hashCode()和equals()這樣的方法。Lombok會在編譯時修改插入程式碼,因此不會影響任何執行時的效能。

  • 安裝Lombok

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

Jetbrains IntelliJ IDEA使用者需要安裝外掛

  • Lombok用法

  • @Data註解

@Data直接修飾POJO或者beans, getter所有的變數,setter所有不為final的變數。如果不需要預設的生成方式,直接填寫你需要的annotation的就可以了。預設生成的所有的annotation都是public的,如果需要不同許可權修飾符可以使用AccessLevel選項。當然@Data 也可以使用staticConstructor選項生成一個靜態方法。包含了以下幾種註解:

註解 生成 選項
@Getter / @Setter getter / setter 方法 /
@ToString toString方法 includeFieldNames,toString方法是否輸出成員變數名,可選值true/false,預設為true
@EqualsAndHashCode equals()和hashCode()方法 /
@RequiredArgsConstructor   包含非空引數(未初始化的final成員變數或者@NonNull成員變數)的構造器 /

POJO添加了@Data註解,相當於添加了上述五個註解,示例程式碼:

@Data
public class DataExample {
    private final String name;

    private final String name1 = "zhuoli";

    @NonNull
    private String name2;

    @NonNull
    private String name3 = "zhuoli";

    @Setter(AccessLevel.PACKAGE)
    private int age;

    private double score;

    private String[] tags;

    @ToString(includeFieldNames=false)
    @Data(staticConstructor="of")
    public static class Exercise<T> {
        private final String name;
        private final T value;
    }
}

編譯後生成的class檔案:

public class DataExample {
    private final String name;
    private final String name1 = "zhuoli";
    @NonNull
    private String name2;
    @NonNull
    private String name3 = "zhuoli";
    private int age;
    private double score;
    private String[] tags;

    public DataExample(String name, @NonNull String name2) {
        if (name2 == null) {
            throw new NullPointerException("name2");
        } else {
            this.name = name;
            this.name2 = name2;
        }
    }

    public String getName() {
        return this.name;
    }

    public String getName1() {
        this.getClass();
        return "zhuoli";
    }

    @NonNull
    public String getName2() {
        return this.name2;
    }

    @NonNull
    public String getName3() {
        return this.name3;
    }

    public int getAge() {
        return this.age;
    }

    public double getScore() {
        return this.score;
    }

    public String[] getTags() {
        return this.tags;
    }

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

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

    public void setScore(double score) {
        this.score = score;
    }

    public void setTags(String[] tags) {
        this.tags = tags;
    }

    public boolean equals(Object o) {
        //省略
    }

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

    public int hashCode() {
    }

    public String toString() {
        return "DataExample(name=" + this.getName() + ", name1=" + this.getName1() + ", name2=" + this.getName2() + ", name3=" + this.getName3() + ", age=" + this.getAge() + ", score=" + this.getScore() + ", tags=" + Arrays.deepToString(this.getTags()) + ")";
    }

    void setAge(int age) {
        this.age = age;
    }

    public static class Exercise<T> {
        private final String name;
        private final T value;

        public String toString() {
            return "DataExample.Exercise(" + this.getName() + ", " + this.getValue() + ")";
        }

        private Exercise(String name, T value) {
            this.name = name;
            this.value = value;
        }

        public static <T> DataExample.Exercise<T> of(String name, T value) {
            return new DataExample.Exercise(name, value);
        }

        public String getName() {
            return this.name;
        }

        public T getValue() {
            return this.value;
        }

        public boolean equals(Object o) {
            //省略
        }

        protected boolean canEqual(Object other) {
            return other instanceof DataExample.Exercise;
        }

        public int hashCode() {
            //省略
        }
    }
}

值得注意的是name1和name3因為已經初始化,生成的建構函式引數中不包含這兩項,@Setter使用了AccessLevel.PACKAGE選項,所以生成的setter方法美加許可權修飾符,為包訪問許可權。Exercise內部@ToString註解添加了includeFieldNames=false選項,所以生成的toString方法不包含變數名,@Data添加了staticConstructor=”of”屬性,所以生成了靜態建構函式of。

  • @Value註解

@Value與@Data的主要區別就是,如果成員變數不加@NonFinal ,編譯後生成的類所有的成員變數都會是final的,相應的,就沒有set方法了,示例程式碼:

@Value
public class ValueExample {
    private String name;

    @Wither(AccessLevel.PACKAGE)
    @NonFinal
    private int age;

    private double score;
    protected String[] tags;

    @ToString
    @Value(staticConstructor="of")
    public static class Exercise<T> {
        String name;
        T value;
    }
}

編譯後生成的class檔案:

public final class ValueExample {
    private final String name;
    private int age;
    private final double score;
    protected final String[] tags;

    public ValueExample(String name, int age, double score, String[] tags) {
        this.name = name;
        this.age = age;
        this.score = score;
        this.tags = tags;
    }

    public String getName() {
        return this.name;
    }

    public int getAge() {
        return this.age;
    }

    public double getScore() {
        return this.score;
    }

    public String[] getTags() {
        return this.tags;
    }

    public boolean equals(Object o) {
        //省略
    }

    public int hashCode() {
        //省略
    }

    public String toString() {
        return "ValueExample(name=" + this.getName() + ", age=" + this.getAge() + ", score=" + this.getScore() + ", tags=" + Arrays.deepToString(this.getTags()) + ")";
    }

    ValueExample withAge(int age) {
        return this.age == age ? this : new ValueExample(this.name, age, this.score, this.tags);
    }

    public static final class Exercise<T> {
        private final String name;
        private final T value;

        private Exercise(String name, T value) {
            this.name = name;
            this.value = value;
        }

        public static <T> ValueExample.Exercise<T> of(String name, T value) {
            return new ValueExample.Exercise(name, value);
        }

        public String getName() {
            return this.name;
        }

        public T getValue() {
            return this.value;
        }

        public boolean equals(Object o) {
            //省略
        }

        public int hashCode() {
            //省略
        }

        public String toString() {
            return "ValueExample.Exercise(name=" + this.getName() + ", value=" + this.getValue() + ")";
        }
    }
}

可以看到,除了@NonFinal標註的成員變數age,其他成員變數編譯後都變成final,但是age成員變量出現在了建構函式中(這一點跟@RequiredArgsConstructor註解有點違背,我也每太看懂)。同時生成了一個包訪問許可權的WithAge方法,返回一個類例項,不多說,看一下程式碼就能看懂

  • @Builder註解

@Builder註解標註在類、構造器或方法上,可生成各種builder APIs,允許開發者以如下方式呼叫:

Person.builder().name("Adam Savage").city("San Francisco").job("Mythbusters").job("Unchained Reaction").build();
@Builder
@ToString
@Getter
public class BuilderExample {
    /*如果created沒有被build賦值,則取預設值*/
    @Builder.Default
    private long created = System.currentTimeMillis();

    private String name;

    private int age;

    /*表明成員變數是collection*/
    @Singular
    private Set<String> occupations;
}

呼叫示例:

invoke com.pdd.service.ad.biz.common.contract.service.AccountBalanceRemindingService.createBalanceRemindingRecord({"mallId": 565656, "remindValue": 9988, "mobile": "13156897423", "isMsgboxRemind": 1, "isSmsRemind": 1, "isAutoCharge": 1, "singleChargeAmount": 100000})

ad-biz-integration-api.finance.contract.appkey
ad-biz-integration-api.finance.contract.secret

invoke com.pdd.service.ad.biz.common.contract.service.AccountBalanceRemindingService.updateBalanceRemindingRecord({"mallId":236639, "isAutoCharge":1, "singleChargeAmount":50000, "dayChargeAmount":100000})
I2ARedisValueV6
I2ARedisValueV6

DataExample(name=" + this.getName() + ", name1=" + this.getName1() + ", name2=" + this.getName2() + ", name3=" + this.getName3() + ", age=" + this.getAge() + ", score=" + this.getScore() + ", tags=" + Arrays.deepToString(this.getTags()) + ")
DataExample.Exercise(name=" + this.getName() + ", value=" + this.getValue() + ")"

@Test
public void builderTest(){
    BuilderExample builderExample = BuilderExample.builder().name("zhuoli").age(22).occupation("haha").build();
    assertThat(builderExample.getOccupations(), containsInAnyOrder("haha"));

    BuilderExample builderExample1 = BuilderExample.builder().occupations(Sets.newHashSet("this", "is", "builder")).build();
    assertThat(builderExample1.getOccupations(), containsInAnyOrder("this", "is", "builder"));
}
  • @Log註解

@Log註解標註在類上,並根據不同的註解生成不同型別的日誌logger物件,但是例項名稱都是log,修飾屬性都是private static final,目前支援以下幾種logger:

註解 生成物件
@CommonsLog org.apache.commons.logging.Log
@JBossLog org.jboss.logging.Logger
@Log java.util.logging.Logger
@Log4j org.apache.log4j.Logger
@Log4j2 org.apache.log4j.LogManager
@Slf4j org.slf4j.Logger
@XSlf4j org.slf4j.ext.XLogger

如@CommonsLog相當於:

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

@Slf4j相當於:

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

更多細節,請參考官方文件

  • @XXXConstructor註解

該系列註解提供自動生成建構函式的功能,主要分為以下三類:

註解 生成構造器
@NoArgsConstructor 無參構造器
@RequiredArgsConstructor 包含非空引數(未初始化的final成員變數或者@NonNull成員變數)的構造器
@AllArgsConstructor 包含所有非static成員、未初始化的final成員的構造器
@AllArgsConstructor
public class AllArgsConstructorExample {
    private Integer x;

    private Integer y;

    private static Integer z;

    private final Integer z1;

    private final Integer z2 = 2;

    private static final Integer z3 = 1;
}

編譯後生成的程式碼:

public class AllArgsConstructorExample {
    private Integer x;
    private Integer y;
    private static Integer z;
    private final Integer z1;
    private final Integer z2 = 2;
    private static final Integer z3 = 1;

    public AllArgsConstructorExample(Integer x, Integer y, Integer z1) {
        this.x = x;
        this.y = y;
        this.z1 = z1;
    }
}

static成員變數z、z3,已初始化的成員變數z2沒有出現在建構函式中

  • @NonNull註解

@NonNull註解提供成員變數的非空檢查

public NonNullExample(@NonNull Person person) {
    super("Hello");
    this.name = person.getName();
}
// 等同於
public NonNullExample(Person person) {
    super("Hello");
    if (person == null) {
      throw new NullPointerException("person");
    }
    this.name = person.getName();
}
  • @SneakyThrows註解

@SneakyThrows註解的方法會自動丟擲受檢異常,而無需顯式在方法上使用throws語句

  • @Synchronized註解

@Synchronized註解標註在方法上,將方法宣告為同步的,並自動加鎖,而鎖物件是一個私有的屬性$lock或$LOCK。示例程式碼:

public class SynchronizedExample {
  private final Object readLock = new Object();
  @Synchronized
  public static void hello() {
    System.out.println("world");
  }
  @Synchronized
  public int answerToLife() {
    return 42;
  }
  @Synchronized("readLock")
  public void foo() {
    System.out.println("bar");
  }
}
// 等同於
public class SynchronizedExample {
  private static final Object $LOCK = new Object[0];
  private final Object $lock = new Object[0];
  private final Object readLock = new Object();
  public static void hello() {
    synchronized($LOCK) {
      System.out.println("world");
    }
  }
  public int answerToLife() {
    synchronized($lock) {
      return 42;
    }
  }
  public void foo() {
    synchronized(readLock) {
      System.out.println("bar");
    }
  }
}
  • @Getter(lazy=true)註解

@Getter(lazy=true)註解可以替代經典的Double Check Lock樣板程式碼,實現單例,示例程式碼:

public class GetterLazyExample {
  @Getter(lazy=true) private final double[] cached = expensive();
  private double[] expensive() {
    double[] result = new double[1000000];
    for (int i = 0; i < result.length; i++) {
      result[i] = Math.asin(i);
    }
    return result;
  }
}
// 等同於
 public class GetterLazyExample {
  private final java.util.concurrent.AtomicReference<java.lang.Object> cached = new java.util.concurrent.AtomicReference<>();
  public double[] getCached() {
    java.lang.Object value = this.cached.get();
    if (value == null) {
      synchronized(this.cached) {
        value = this.cached.get();
        if (value == null) {
          final double[] actualValue = expensive();
          value = actualValue == null ? this.cached : actualValue;
          this.cached.set(value);
        }
      }
    }
    return (double[])(value == this.cached ? null : value);
  }
  private double[] expensive() {
    double[] result = new double[1000000];
    for (int i = 0; i < result.length; i++) {
      result[i] = Math.asin(i);
    }
    return result;
  }
}