摘要
最近 IDEA 2020 最初一个版本公布了,曾经内置了 Lombok 插件,SpringBoot 2.1.x 之后的版本也在 Starter 中内置了 Lombok 依赖。为什么他们都要反对 Lombok 呢?明天我来讲讲 Lombok 的应用,看看它有何神奇之处!
Lombok 简介
Lombok 是一款 Java 代码性能加强库,在 Github 上已有 9.8k+Star。它会主动集成到你的编辑器和构建工具中,从而使你的 Java 代码更加生动有趣。通过 Lombok 的注解,你能够不必再写 getter、setter、equals 等办法,Lombok 将在编译时为你主动生成。
Lombok 集成
首先咱们须要在 IDEA 中装置好 Lombok 插件,如果你应用的是最新版 IDEA 2020.3,则 Lombok 插件曾经内置,无需装置。
之后在我的项目的 pom.xml 文件中增加 Lombok 依赖,SpringBoot 2.1.x 版本后无需指定 Lombok 版本,SpringBoot 在 spring-boot-dependencies
中曾经内置。
<!--lombok 依赖 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
复制代码
Lombok 应用
Lombok 中有很多注解,这些注解使得咱们能够更加不便的编写 Java 代码,上面介绍下这些注解的应用。
val
应用 val 注解能够取代任意类型作为局部变量,这样咱们就不必写简单的 ArrayList 和 Map.Entry 类型了,具体例子如下。
/**
* Created by macro on 2020/12/16.
*/
public class ValExample {public static void example() {
//val 代替 ArrayList<String> 和 String 类型
val example = new ArrayList<String>();
example.add("Hello World!");
val foo = example.get(0);
System.out.println(foo.toLowerCase());
}
public static void example2() {
//val 代替 Map.Entry<Integer,String> 类型
val map = new HashMap<Integer, String>();
map.put(0, "zero");
map.put(5, "five");
for (val entry : map.entrySet()) {System.out.printf("%d: %sn", entry.getKey(), entry.getValue());
}
}
public static void main(String[] args) {example();
example2();}
}
复制代码
当咱们应用了 val 注解后,Lombok 会从局部变量的初始化表达式推断出具体类型,编译后会生成如下代码。
public class ValExample {public ValExample() { }
public static void example() {ArrayList<String> example = new ArrayList();
example.add("Hello World!");
String foo = (String)example.get(0);
System.out.println(foo.toLowerCase());
}
public static void example2() {HashMap<Integer, String> map = new HashMap();
map.put(0, "zero");
map.put(5, "five");
Iterator var1 = map.entrySet().iterator();
while(var1.hasNext()) {Entry<Integer, String> entry = (Entry)var1.next();
System.out.printf("%d: %sn", entry.getKey(), entry.getValue());
}
}
}
复制代码
@NonNull
在办法上应用 @NonNull 注解能够做非空判断,如果传入空值的话会间接抛出 NullPointerException。
/**
* Created by macro on 2020/12/16.
*/
public class NonNullExample {
private String name;
public NonNullExample(@NonNull String name){this.name = name;}
public static void main(String[] args) {new NonNullExample("test");
// 会抛出 NullPointerException
new NonNullExample(null);
}
}
复制代码
编译后会在结构器中增加非空判断,具体代码如下。
public class NonNullExample {
private String name;
public NonNullExample(@NonNull String name) {if (name == null) {throw new NullPointerException("name is marked non-null but is null");
} else {this.name = name;}
}
public static void main(String[] args) {new NonNullExample("test");
new NonNullExample((String)null);
}
}
复制代码
@Cleanup
当咱们在 Java 中应用资源时,不可避免地须要在应用后敞开资源。应用 @Cleanup 注解能够主动敞开资源。
/**
* Created by macro on 2020/12/16.
*/
public class CleanupExample {public static void main(String[] args) throws IOException {
String inStr = "Hello World!";
// 应用输入输出流主动敞开,无需编写 try catch 和调用 close()办法
@Cleanup ByteArrayInputStream in = new ByteArrayInputStream(inStr.getBytes("UTF-8"));
@Cleanup ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] b = new byte[1024];
while (true) {int r = in.read(b);
if (r == -1) break;
out.write(b, 0, r);
}
String outStr = out.toString("UTF-8");
System.out.println(outStr);
}
}
复制代码
编译后 Lombok 会生成如下代码。
public class CleanupExample {public CleanupExample() { }
public static void main(String[] args) throws IOException {
String inStr = "Hello World!";
ByteArrayInputStream in = new ByteArrayInputStream(inStr.getBytes("UTF-8"));
try {ByteArrayOutputStream out = new ByteArrayOutputStream();
try {byte[] b = new byte[1024];
while(true) {int r = in.read(b);
if (r == -1) {String outStr = out.toString("UTF-8");
System.out.println(outStr);
return;
}
out.write(b, 0, r);
}
} finally {if (Collections.singletonList(out).get(0) != null) {out.close();
}
}
} finally {if (Collections.singletonList(in).get(0) != null) {in.close();
}
}
}
}
复制代码
@Getter/@Setter
有了 @Getter/@Setter 注解,咱们再也不必编写 getter/setter 办法了。试想下之前即便咱们应用 IDEA 主动生成 getter/setter 办法,如果类属性的类型和名称改了,又要从新生成 getter/setter 办法也是一件很麻烦的事件。
/**
* Created by macro on 2020/12/17.
*/
public class GetterSetterExample {
@Getter
@Setter
private String name;
@Getter
@Setter(AccessLevel.PROTECTED)
private Integer age;
public static void main(String[] args) {GetterSetterExample example = new GetterSetterExample();
example.setName("test");
example.setAge(20);
System.out.printf("name:%s age:%d",example.getName(),example.getAge());
}
}
复制代码
编译后 Lombok 会生成如下代码。
public class GetterSetterExample {
private String name;
private Integer age;
public GetterSetterExample() {}
public String getName() {return this.name;}
public void setName(final String name) {this.name = name;}
public Integer getAge() {return this.age;}
protected void setAge(final Integer age) {this.age = age;}
}
复制代码
@ToString
把所有类属性都编写到 toString 办法中不便打印日志,是一件如许枯燥无味的事件。应用 @ToString 注解能够主动生成 toString 办法,默认会蕴含所有类属性,应用 @ToString.Exclude 注解能够排除属性的生成。
/**
* Created by macro on 2020/12/17.
*/
@ToString
public class ToStringExample {
@ToString.Exclude
private Long id;
private String name;
private Integer age;
public ToStringExample(Long id,String name,Integer age){
this.id =id;
this.name = name;
this.age = age;
}
public static void main(String[] args) {ToStringExample example = new ToStringExample(1L,"test",20);
// 主动实现 toString 办法,输入 ToStringExample(name=test, age=20)
System.out.println(example);
}
}
复制代码
编译后 Lombok 会生成如下代码。
public class ToStringExample {
private Long id;
private String name;
private Integer age;
public ToStringExample(Long id, String name, Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
public String toString() {return "ToStringExample(name=" + this.name + ", age=" + this.age + ")";
}
}
复制代码
@EqualsAndHashCode
应用 @EqualsAndHashCode 注解能够主动生成 hashCode 和 equals 办法,默认蕴含所有类属性,应用 @EqualsAndHashCode.Exclude 能够排除属性的生成。
/**
* Created by macro on 2020/12/17.
*/
@Getter
@Setter
@EqualsAndHashCode
public class EqualsAndHashCodeExample {
private Long id;
@EqualsAndHashCode.Exclude
private String name;
@EqualsAndHashCode.Exclude
private Integer age;
public static void main(String[] args) {EqualsAndHashCodeExample example1 = new EqualsAndHashCodeExample();
example1.setId(1L);
example1.setName("test");
example1.setAge(20);
EqualsAndHashCodeExample example2 = new EqualsAndHashCodeExample();
example2.setId(1L);
//equals 办法只比照 id,返回 true
System.out.println(example1.equals(example2));
}
}
复制代码
编译后 Lombok 会生成如下代码。
public class EqualsAndHashCodeExample {
private Long id;
private String name;
private Integer age;
public EqualsAndHashCodeExample() {}
public boolean equals(final Object o) {if (o == this) {return true;} else if (!(o instanceof EqualsAndHashCodeExample)) {return false;} else {EqualsAndHashCodeExample other = (EqualsAndHashCodeExample)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;}
return true;
}
}
}
protected boolean canEqual(final Object other) {return other instanceof EqualsAndHashCodeExample;}
public int hashCode() {
int PRIME = true;
int result = 1;
Object $id = this.getId();
int result = result * 59 + ($id == null ? 43 : $id.hashCode());
return result;
}
}
复制代码
@XxConstructor
应用 @XxConstructor 注解能够主动生成构造方法,有 @NoArgsConstructor、@RequiredArgsConstructor 和 @AllArgsConstructor 三个注解能够应用。
- @NoArgsConstructor:生成无参构造函数。
- @RequiredArgsConstructor:生成蕴含必须参数的构造函数,应用 @NonNull 注解的类属性为必须参数。
- @AllArgsConstructor:生成蕴含所有参数的构造函数。
/**
* Created by macro on 2020/12/17.
*/
@NoArgsConstructor
@RequiredArgsConstructor(staticName = "of")
@AllArgsConstructor
public class ConstructorExample {
@NonNull
private Long id;
private String name;
private Integer age;
public static void main(String[] args) {
// 无参结构器
ConstructorExample example1 = new ConstructorExample();
// 全副参数结构器
ConstructorExample example2 = new ConstructorExample(1L,"test",20);
//@NonNull 注解的必须参数结构器
ConstructorExample example3 = ConstructorExample.of(1L);
}
}
复制代码
编译后 Lombok 会生成如下代码。
public class ConstructorExample {
@NonNull
private Long id;
private String name;
private Integer age;
public ConstructorExample() {}
private ConstructorExample(@NonNull final Long id) {if (id == null) {throw new NullPointerException("id is marked non-null but is null");
} else {this.id = id;}
}
public static ConstructorExample of(@NonNull final Long id) {return new ConstructorExample(id);
}
public ConstructorExample(@NonNull final Long id, final String name, final Integer age) {if (id == null) {throw new NullPointerException("id is marked non-null but is null");
} else {
this.id = id;
this.name = name;
this.age = age;
}
}
}
复制代码
@Data
@Data 是一个方便使用的组合注解,是 @ToString、@EqualsAndHashCode、@Getter、@Setter 和 @RequiredArgsConstructor 的组合体。
/**
* Created by macro on 2020/12/17.
*/
@Data
public class DataExample {
@NonNull
private Long id;
@EqualsAndHashCode.Exclude
private String name;
@EqualsAndHashCode.Exclude
private Integer age;
public static void main(String[] args) {
//@RequiredArgsConstructor 已失效
DataExample example1 = new DataExample(1L);
//@Getter @Setter 已失效
example1.setName("test");
example1.setAge(20);
//@ToString 已失效
System.out.println(example1);
DataExample example2 = new DataExample(1L);
//@EqualsAndHashCode 已失效
System.out.println(example1.equals(example2));
}
}
复制代码
编译后 Lombok 会生成如下代码。
public class DataExample {
@NonNull
private Long id;
private String name;
private Integer age;
public DataExample(@NonNull final Long id) {if (id == null) {throw new NullPointerException("id is marked non-null but is null");
} else {this.id = id;}
}
@NonNull
public Long getId() {return this.id;}
public String getName() {return this.name;}
public Integer getAge() {return this.age;}
public void setId(@NonNull final Long id) {if (id == null) {throw new NullPointerException("id is marked non-null but is null");
} else {this.id = id;}
}
public void setName(final String name) {this.name = name;}
public void setAge(final Integer age) {this.age = age;}
public boolean equals(final Object o) {if (o == this) {return true;} else if (!(o instanceof DataExample)) {return false;} else {DataExample other = (DataExample)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;}
return true;
}
}
}
protected boolean canEqual(final Object other) {return other instanceof DataExample;}
public int hashCode() {
int PRIME = true;
int result = 1;
Object $id = this.getId();
int result = result * 59 + ($id == null ? 43 : $id.hashCode());
return result;
}
public String toString() {return "DataExample(id=" + this.getId() + ", name=" + this.getName() + ", age=" + this.getAge() + ")";
}
}
复制代码
@Value
应用 @Value 注解能够把类申明为不可变的,申明后此类相当于 final 类,无奈被继承,其属性也会变成 final 属性。
/**
* Created by macro on 2020/12/17.
*/
@Value
public class ValueExample {
private Long id;
private String name;
private Integer age;
public static void main(String[] args) {
// 只能应用全参结构器
ValueExample example = new ValueExample(1L,"test",20);
// example.setName("andy") // 没有生成 setter 办法,会报错
// example.name="andy" // 字段被设置为 final 类型,会报错
}
}
复制代码
编译后 Lombok 会生成如下代码。
public final class ValueExample {
private final Long id;
private final String name;
private final Integer age;
public static void main(String[] args) {new ValueExample(1L, "test", 20);
}
public ValueExample(final Long id, final String name, final Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
public Long getId() {return this.id;}
public String getName() {return this.name;}
public Integer getAge() {return this.age;}
}
复制代码
@Builder
应用 @Builder 注解能够通过建造者模式来创建对象,建造者模式加链式调用,创建对象太不便了!
/**
* Created by macro on 2020/12/17.
*/
@Builder
@ToString
public class BuilderExample {
private Long id;
private String name;
private Integer age;
public static void main(String[] args) {BuilderExample example = BuilderExample.builder()
.id(1L)
.name("test")
.age(20)
.build();
System.out.println(example);
}
}
复制代码
编译后 Lombok 会生成如下代码。
public class BuilderExample {
private Long id;
private String name;
private Integer age;
BuilderExample(final Long id, final String name, final Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
public static BuilderExample.BuilderExampleBuilder builder() {return new BuilderExample.BuilderExampleBuilder();
}
public String toString() {return "BuilderExample(id=" + this.id + ", name=" + this.name + ", age=" + this.age + ")";
}
public static class BuilderExampleBuilder {
private Long id;
private String name;
private Integer age;
BuilderExampleBuilder() {}
public BuilderExample.BuilderExampleBuilder id(final Long id) {
this.id = id;
return this;
}
public BuilderExample.BuilderExampleBuilder name(final String name) {
this.name = name;
return this;
}
public BuilderExample.BuilderExampleBuilder age(final Integer age) {
this.age = age;
return this;
}
public BuilderExample build() {return new BuilderExample(this.id, this.name, this.age);
}
public String toString() {return "BuilderExample.BuilderExampleBuilder(id=" + this.id + ", name=" + this.name + ", age=" + this.age + ")";
}
}
}
复制代码
@SneakyThrows
还在手动捕捉并抛出异样?应用 @SneakyThrows 注解主动实现试试!
/**
* Created by macro on 2020/12/17.
*/
public class SneakyThrowsExample {
// 主动抛出异样,无需解决
@SneakyThrows(UnsupportedEncodingException.class)
public static byte[] str2byte(String str){return str.getBytes("UTF-8");
}
public static void main(String[] args) {
String str = "Hello World!";
System.out.println(str2byte(str).length);
}
}
复制代码
编译后 Lombok 会生成如下代码。
public class SneakyThrowsExample {public SneakyThrowsExample() { }
public static byte[] str2byte(String str) {
try {return str.getBytes("UTF-8");
} catch (UnsupportedEncodingException var2) {throw var2;}
}
}
复制代码
@Synchronized
当咱们在多个线程中拜访同一资源时,往往会呈现线程平安问题,以前咱们往往应用 synchronized 关键字润饰办法来实现同步拜访。应用 @Synchronized 注解同样能够实现同步拜访。
package com.macro.mall.tiny.example;
import lombok.*;
/**
* Created by macro on 2020/12/17.
*/
@Data
public class SynchronizedExample {
@NonNull
private Integer count;
@Synchronized
@SneakyThrows
public void reduceCount(Integer id) {if (count > 0) {Thread.sleep(500);
count--;
System.out.println(String.format("thread-%d count:%d", id, count));
}
}
public static void main(String[] args) {
// 增加 @Synchronized 三个线程能够同步调用 reduceCount 办法
SynchronizedExample example = new SynchronizedExample(20);
new ReduceThread(1, example).start();
new ReduceThread(2, example).start();
new ReduceThread(3, example).start();}
@RequiredArgsConstructor
static class ReduceThread extends Thread {
@NonNull
private Integer id;
@NonNull
private SynchronizedExample example;
@Override
public void run() {while (example.getCount() > 0) {example.reduceCount(id);
}
}
}
}
复制代码
编译后 Lombok 会生成如下代码。
public class SynchronizedExample {private final Object $lock = new Object[0];
@NonNull
private Integer count;
public void reduceCount(Integer id) {
try {synchronized(this.$lock) {if (this.count > 0) {Thread.sleep(500L);
Integer var3 = this.count;
Integer var4 = this.count = this.count - 1;
System.out.println(String.format("thread-%d count:%d", id, this.count));
}
}
} catch (Throwable var7) {throw var7;}
}
}
复制代码
@With
应用 @With 注解能够实现对原对象进行克隆,并扭转其一个属性,应用时须要指定全参构造方法。
@With
@AllArgsConstructor
public class WithExample {
private Long id;
private String name;
private Integer age;
public static void main(String[] args) {WithExample example1 = new WithExample(1L, "test", 20);
WithExample example2 = example1.withAge(22);
// 将原对象进行 clone 并设置 age,返回 false
System.out.println(example1.equals(example2));
}
}
复制代码
编译后 Lombok 会生成如下代码。
public class WithExample {
private Long id;
private String name;
private Integer age;
public WithExample withId(final Long id) {return this.id == id ? this : new WithExample(id, this.name, this.age);
}
public WithExample withName(final String name) {return this.name == name ? this : new WithExample(this.id, name, this.age);
}
public WithExample withAge(final Integer age) {return this.age == age ? this : new WithExample(this.id, this.name, age);
}
public WithExample(final Long id, final String name, final Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
}
复制代码
@Getter(lazy=true)
当咱们获取某一个属性比拟耗费资源时,能够给 @Getter 增加 lazy=true
属性实现懒加载,会生成 Double Check Lock 样板代码对属性进行懒加载。
/**
* Created by macro on 2020/12/17.
*/
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 static void main(String[] args) {
// 应用 Double Check Lock 样板代码对属性进行懒加载
GetterLazyExample example = new GetterLazyExample();
System.out.println(example.getCached().length);
}
}
复制代码
编译后 Lombok 会生成如下代码。
public class GetterLazyExample {private final AtomicReference<Object> cached = new AtomicReference();
public GetterLazyExample() {}
private double[] expensive() {double[] result = new double[1000000];
for(int i = 0; i < result.length; ++i) {result[i] = Math.asin((double)i);
}
return result;
}
public double[] getCached() {Object value = this.cached.get();
if (value == null) {synchronized(this.cached) {value = this.cached.get();
if (value == null) {double[] actualValue = this.expensive();
value = actualValue == null ? this.cached : actualValue;
this.cached.set(value);
}
}
}
return (double[])((double[])(value == this.cached ? null : value));
}
}
复制代码
@Log
应用 @Log 注解,能够间接生成日志对象 log,通过 log 对象能够间接打印日志。
/**
* Created by macro on 2020/12/17.
*/
@Log
public class LogExample {public static void main(String[] args) {log.info("level info");
log.warning("level warning");
log.severe("level severe");
}
}
复制代码
编译后 Lombok 会生成如下代码。
public class LogExample {private static final Logger log = Logger.getLogger(LogExample.class.getName());
public LogExample() {}
public static void main(String[] args) {log.info("level info");
log.warning("level warning");
log.severe("level severe");
}
}
复制代码
@Slf4j
应用 Lombok 生成日志对象时,依据应用日志实现的不同,有多种注解能够应用。比方 @Log、@Log4j、@Log4j2、@Slf4j 等。
/**
* Created by macro on 2020/12/17.
*/
@Slf4j
public class LogSlf4jExample {public static void main(String[] args) {log.info("level:{}","info");
log.warn("level:{}","warn");
log.error("level:{}", "error");
}
}
复制代码
编译后 Lombok 会生成如下代码。
public class LogSlf4jExample {private static final Logger log = LoggerFactory.getLogger(LogSlf4jExample.class);
public LogSlf4jExample() {}
public static void main(String[] args) {log.info("level:{}", "info");
log.warn("level:{}", "warn");
log.error("level:{}", "error");
}
}
复制代码
Lombok 原理
如果 IDEA 不装置 Lombok 插件的话,咱们关上应用 Lombok 的我的项目是无奈通过编译的。装了当前 IDEA 才会提醒咱们 Lombok 为咱们生成的办法和属性。
应用了 @Data 注解当前,查看类构造能够发现 getter、setter、toString 等办法。
关上 target 目录下的 .class
文件,咱们能够看到 Lombok 为咱们生成的代码,可见 Lombok 是通过解析注解,而后在编译时生成代码来实现 Java 代码的性能加强的。
参考资料
《2020 最新 Java 根底精讲视频教程和学习路线!》
官网文档:https://projectlombok.org/fea…
链接:https://juejin.cn/post/691147…