文章目录
- 1.Lombok 是什么
- 2.安装 Lombok
- 3.Spring Boot 集成 Lombok
- 4.使用 Lombok
- 4.1 注解一览表
- 4.2 部分使用介绍
- @Getter(lazy=true)
- @Value
- @Builder
- @SuperBuilder
- @SneakyThrows
- @Slf4j
- @Cleanup
- @NonNull
- @With
- @Synchronized
- val
在 Java 开发领域中,Lombok 插件已经成为一个非常流行的代码库。该插件让 Java 开发更加便捷、高效,因此提高了开发者的生产力。
1.Lombok 是什么
Lombok 是一款 Java 开发工具,它可以通过注解来帮助程序员在编译时自动生成 Java 代码,从而简化 Java 开发过程。具体来说他会通过解析注解,然后在编译时生成代码来实现 Java 代码的功能增强。
Lombok 插件产生的主要原因是 Java 语言臃肿的语法,需要大量的样板代码,以及冗长臃肿的 getter 和 setter 方法。当你的模型层非常大时,手动编写所有这些代码会变得非常繁琐和无聊。因此,Lombok 插件为我们自动生成 Java 代码并帮助优化 Java 开发过程,提高效率。
2.安装 Lombok
打开 IDEA 设置页面:
在插件页面搜索“Lombok”安装即可:
注意:使用 Lombok 必须要在 IDE 中安装 Lombok 插件,以使得 IDE 能正确识别和使用 Lombok 注解。
3.Spring Boot 集成 Lombok
为了使用 Lombok 插件,我们需要在项目中设置依赖。下面是一个使用 Maven 添加 Lombok 的 pom.xml 文件的例子:
<!--Lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
注意:SpringBoot 2.1.x 版本后无需指定 Lombok 版本,SpringBoot 在
spring-boot-dependencies
中已经内置了该依赖进行管理。
4.使用 Lombok
4.1 注解一览表
以下是 Lombok 提供的一些主要注解及其功能:
注解 | 功能 |
---|---|
@Getter | 为字段生成 getter 方法 |
@Setter | 为字段生成 setter 方法 |
@Data | 为字段生成 getter 和 setter 方法,还包括 equals, hashCode, toString 方法 |
@ToString | 为类生成 toString 方法 |
@EqualsAndHashCode | 为类生成 equals 和 hashCode 方法 |
@NoArgsConstructor | 为类生成无参构造器 |
@AllArgsConstructor | 为类生成包含所有字段的构造器 |
@RequiredArgsConstructor | 为类生成包含必须字段(final 和带有 @NonNull 注解的字段)的构造器 |
@Value | 为类生成只读属性(final),包括 getter, equals, hashCode, toString 方法 |
@Builder | 为类实现 Builder 模式 |
@SneakyThrows | 用于方法上,可以让方法抛出被检查的异常,而不需要显式地在方法上使用 throws 关键字 |
@Slf4j | 在类中添加一个名为 ‘log’ 的 SLF4J 日志器对象 |
@Cleanup | 自动管理资源,用于自动调用 close 方法释放资源 |
@NonNull | 用于检查方法或构造器的参数是否为 null,如果为 null 就会抛出 NullPointerException |
@Delegate | 自动生成委托(delegate)方法,用来将调用转发到指定的字段或方法 |
@With | 为字段生成返回更新了某个字段值的新对象的方法 |
@SuperBuilder | 用于更复杂的继承情况下的 Builder 模式 |
@Synchronized | Lombok 提供的同步锁,用于方法,将会同步在一个私有的字段上,如果是静态方法,则同步在一个私有的静态字段上 |
val | 定义一个局部变量并立即为其赋值,这个变量为 final 类型,不能被修改 |
4.2 部分使用介绍
上述注释大多数使用看功能描述基本都能理解,下面挑出一些不太容易理解的进行使用说明。
@Getter(lazy=true)
@Getter(lazy=true)
为字段生成延迟初始化的 getter 方法。这个 getter 方法在第一次被调用时初始化字段,并将结果缓存。一般用于需要获取的某一个属性比较消耗资源时,便可以通过该注解添加 lazy=true
属性实现懒加载,这会生成 Double Check Lock 样板代码对属性进行懒加载。
在 Java 中,“double check lock”(双重检查锁)模式是一种常用的多线程并发编程技巧,主要用于延迟初始化并确保只有一个线程能够初始化资源。当你在 Lombok 中使用
@Getter(lazy=true)
注解时,Lombok 就会生成对应的双重检查锁的代码。这样可以确保字段的延迟初始化,在多线程环境中也能保证线程安全,而且只会初始化一次。
import lombok.Getter;
public class LazyGetterExample {
// 使用双重检查锁对方法进行加锁,确保只有一个线程可以执行 expensive() 方法(懒加载)
@Getter(lazy = true)
private final Double cached = expensive();
private Double expensive() {
// 模拟一个耗时的操作
Double result = null;
for (int i = 0; i < 1000000; i++) {
result = Math.atan(i) * Math.tan(i);
}
return result;
}
public static void main(String[] args) {
LazyGetterExample example = new LazyGetterExample();
System.out.println(example.getCached());
}
}
编译后的代码会是如下这样的:
import java.util.concurrent.atomic.AtomicReference;
public class LazyGetterExample {
private final AtomicReference<Object> cached = new AtomicReference();
public LazyGetterExample() {
}
private Double expensive() {
Double result = null;
for(int i = 0; i < 1000000; ++i) {
result = Math.atan((double)i) * Math.tan((double)i);
}
return result;
}
public static void main(String[] args) {
LazyGetterExample example = new LazyGetterExample();
System.out.println(example.getCached());
}
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));
}
}
@Value
@Value
为类生成只读属性,即所有字段都是 final
,包括 getter
方法、equals
、hashCode
、toString
方法。此时此类相当于 final
类,无法被继承,其属性也会变成 final
属性。
@Value
public class ValueExample {
private String name;
private int age;
public static void main(String[] args) {
// 只能使用全参构造器
ValueExample example = new ValueExample("张三", 18);
// 可以直接获取属性值
System.out.println(example.getName());
System.out.println(example.getAge());
// 不能修改属性值
// example.setName("李四");
// example.setAge(20);
}
}
编译后的代码会是如下这样的:
public final class ValueExample {
private final String name;
private final int age;
public static void main(String[] args) {
ValueExample example = new ValueExample("张三", 18);
System.out.println(example.getName());
System.out.println(example.getAge());
}
public ValueExample(final String name, final int age) {
this.name = name;
this.age = age;
}
public String getName() {
return this.name;
}
public int getAge() {
return this.age;
}
public boolean equals(final Object o) {
if (o == this) {
return true;
} else if (!(o instanceof ValueExample)) {
return false;
} else {
ValueExample other = (ValueExample)o;
if (this.getAge() != other.getAge()) {
return false;
} else {
Object this$name = this.getName();
Object other$name = other.getName();
if (this$name == null) {
if (other$name != null) {
return false;
}
} else if (!this$name.equals(other$name)) {
return false;
}
return true;
}
}
}
public int hashCode() {
int PRIME = true;
int result = 1;
result = result * 59 + this.getAge();
Object $name = this.getName();
result = result * 59 + ($name == null ? 43 : $name.hashCode());
return result;
}
public String toString() {
return "ValueExample(name=" + this.getName() + ", age=" + this.getAge() + ")";
}
}
@Builder
@Builder
为类实现 Builder 设计模式(建造者模式),通常用于链式构造复杂对象。
import lombok.Builder;
import lombok.ToString;
@ToString
@Builder
public class BuilderExample {
private String name;
private int age;
public static void main(String[] args) {
BuilderExample example = BuilderExample.builder().name("张三").age(18).build();
System.out.println(example);
}
}
编译后的代码会是如下这样的:
public class BuilderExample {
private String name;
private int age;
public static void main(String[] args) {
BuilderExample example = builder().name("张三").age(18).build();
System.out.println(example);
}
BuilderExample(final String name, final int age) {
this.name = name;
this.age = age;
}
public static BuilderExampleBuilder builder() {
return new BuilderExampleBuilder();
}
public String toString() {
return "BuilderExample(name=" + this.name + ", age=" + this.age + ")";
}
public static class BuilderExampleBuilder {
private String name;
private int age;
BuilderExampleBuilder() {
}
public BuilderExampleBuilder name(final String name) {
this.name = name;
return this;
}
public BuilderExampleBuilder age(final int age) {
this.age = age;
return this;
}
public BuilderExample build() {
return new BuilderExample(this.name, this.age);
}
public String toString() {
return "BuilderExample.BuilderExampleBuilder(name=" + this.name + ", age=" + this.age + ")";
}
}
}
@SuperBuilder
@SneakyThrows
@SneakyThrows
用于方法上,可以让方法抛出被检查的异常,而不需要显式地在方法上使用 throws
关键字抛出异常。
import lombok.SneakyThrows;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
public class SneakyThrowsExample {
// 自动抛出异常,不需要手动 try catch
@SneakyThrows(UnsupportedEncodingException.class)
public String sneakyMethod(){
return URLEncoder.encode("Example", "Unsupported Encoding");
}
public static void main(String[] args) {
SneakyThrowsExample example = new SneakyThrowsExample();
System.out.println(example.sneakyMethod());
}
}
编译后的代码会是如下这样的:
public class SneakyThrowsExample {
public SneakyThrowsExample() {
}
public String sneakyMethod() {
try {
return URLEncoder.encode("Example", "Unsupported Encoding");
} catch (UnsupportedEncodingException var2) {
throw var2;
}
}
public static void main(String[] args) {
SneakyThrowsExample example = new SneakyThrowsExample();
System.out.println(example.sneakyMethod());
}
}
@Slf4j
使用 Lombok 生成日志对象时,根据使用日志实现的不同,有多种注解可以使用。比如 @Log
、@Log4j
、@Log4j2
、@Slf4j
等。以 @Slf4j
为例,它会在类中添加一个名为 ‘log’ 的 SLF4J 日志器对象。
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class Slf4jExample {
public static void main(String[] args) {
log.info("Hello World");
log.error("Hello World");
log.debug("Hello World");
log.warn("Hello World");
}
}
编译后的代码会是如下这样的:
public class Slf4jExample {
private static final Logger log = LoggerFactory.getLogger(Slf4jExample.class);
public Slf4jExample() {
}
public static void main(String[] args) {
log.info("Hello World");
log.error("Hello World");
log.debug("Hello World");
log.warn("Hello World");
}
}
@Cleanup
@Cleanup
用于自动管理资源,用于自动调用 close()
方法释放资源,避免了手动释放。
public class CleanupExample {
public static void main(String[] args) throws IOException {
@Cleanup InputStream in = Files.newInputStream(Paths.get("file.txt"));
}
}
编译后的代码会是如下这样的:
public class CleanupExample {
public CleanupExample() {
}
public static void main(String[] args) throws IOException {
InputStream in = new FileInputStream("/file.txt");
if (Collections.singletonList(in).get(0) != null) {
in.close();
}
}
}
@NonNull
@NonNull
用于检查方法或构造器的参数是否为 null,如果为 null 就会抛出 NullPointerException
,一般用来做非空判断。
public class NonNullExample {
public String nonNullMethod(@NonNull String string){
return string;
}
public static void main(String[] args) {
NonNullExample example = new NonNullExample();
example.nonNullMethod(null);
}
}
编译后的代码会是如下这样的:
public class NonNullExample {
public NonNullExample() {
}
public String nonNullMethod(@NonNull String string) {
if (string == null) {
throw new NullPointerException("string is marked non-null but is null");
} else {
return string;
}
}
public static void main(String[] args) {
NonNullExample example = new NonNullExample();
example.nonNullMethod((String)null);
}
}
@With
@With
用于为字段生成返回更新了某个字段值的新对象的方法。简单来说,它可以实现对原对象进行克隆,并改变其一个属性,使用时需要指定全参构造方法。
import lombok.AllArgsConstructor;
import lombok.With;
@With
@AllArgsConstructor
public class WithExample {
private String name;
private int age;
public static void main(String[] args) {
// 实例化一个对象
WithExample example = new WithExample("javgo", 18);
System.out.println(example);
// 使用 withName 方法修改 name 属性
WithExample withExample = example.withName("javgo2");
System.out.println(withExample);
}
}
编译后的代码会是如下这样的:
public class WithExample {
private String name;
private int age;
public static void main(String[] args) {
WithExample example = new WithExample("javgo", 18);
System.out.println(example);
WithExample withExample = example.withName("javgo2");
System.out.println(withExample);
}
public WithExample withName(final String name) {
return this.name == name ? this : new WithExample(name, this.age);
}
public WithExample withAge(final int age) {
return this.age == age ? this : new WithExample(this.name, age);
}
public WithExample(final String name, final int age) {
this.name = name;
this.age = age;
}
}
@Synchronized
当我们在多个线程中访问同一资源时,往往会出现线程安全问题,往往我们会使用 synchronized
关键字修饰方法来实现同步访问。而 @Synchronized
是 Lombok 提供的同步锁,用于方法,将会同步在一个私有的字段上,如果是静态方法,则同步在一个私有的静态字段上。
import lombok.Synchronized;
public class SynchronizedExample {
private final Object readLock = new Object();
@Synchronized
public void syncMethod(){
// do something
}
@Synchronized("readLock")
public void customLockMethod() {
// do something
}
public static void main(String[] args) {
SynchronizedExample example = new SynchronizedExample();
// 两个方法都会被锁定
example.syncMethod(); // 锁定的是 this, 即 SynchronizedExample 对象
example.customLockMethod(); // 锁定的是 readLock,即 SynchronizedExample.readLock 对象
}
}
编译后的代码会是如下这样的:
public class SynchronizedExample {
private final Object $lock = new Object[0];
private final Object readLock = new Object();
public SynchronizedExample() {
}
public void syncMethod() {
synchronized(this.$lock) {
;
}
}
public void customLockMethod() {
synchronized(this.readLock) {
;
}
}
public static void main(String[] args) {
SynchronizedExample example = new SynchronizedExample();
example.syncMethod();
example.customLockMethod();
}
}
val
val
用于定义一个任意类型的局部变量并立即为其赋值,这个变量为 final
类型,不能被修改。
import lombok.val;
import java.util.ArrayList;
public class ValExample {
public static void main(String[] args) {
val example = new ArrayList<String>();
example.add("Hello, World!");
}
}
编译后的代码会是如下这样的:
public class ValExample {
public ValExample() {
}
public static void main(String[] args) {
ArrayList<String> example = new ArrayList();
example.add("Hello, World!");
}
}