文章目录
lombok可以通过注解的方式在编译期动态的为类添加方法。
一、配置IDEA中使用lombok
1、导入lombok依赖包。
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
<scope>provided</scope>
</dependency>
如果使用的时gradle构建的项目,不仅需要导入lombok依赖包,还需要指定其为编译期执行。
compileOnly 'org.projectlombok:lombok:1.18.22'
annotationProcessor 'org.projectlombok:lombok:1.18.22'
2、安装并启用lombok插件
3、启用Annotation processing
File --> Settings --> Build, Execution, Deployment --> Compiler -->Annotation Processors --> Enable annotation processing(如图)
二、lombok常用注解
No. | 注解 | 描述 |
---|---|---|
01 | @Setter | 为类的属性提供setter设置方法 |
02 | @Getter | 为类的属性提供getter获取方法 |
03 | @ToString | 提供toString()方法 |
04 | @EqualsAndHashCode | 提供equals()和hashCode()方法 |
05 | @NoArgsConstructor | 无参构造 |
06 | @AllArgsConstructor | 全参构造 |
07 | @RequiredArgsConstructor | 指定参数构造 |
08 | @Cleanup | 注解需要放在流的声明上,实现IO流资源关闭 |
09 | @Data | 相当于@ToString、@EqualsAndHashCode、@Getter以及所有非final字段的@Setter、@RequiredArgsConstructor |
10 | @Builder | 建造者模式 |
11 | @NonNull | 避免NullPointException的异常产生 |
12 | @Value | 用于注解final类 |
13 | @SneakyThrows | 异常处理 |
14 | @Synchronized | 同步方法安全转化 |
15 | @Log | 支持各种logger对象,使用时用对应的注解,如:@Log4J、@Slf4j |
1、@Data注解
新建一个Message类,定义3个属性。
import lombok.Data;
import java.util.Date;
@Data
public class Message {
private Integer id;
private String content;
private Date pubDate;
}
通过反编译工具查看生成的.class
文件:
public class Message {
private Integer id;
private String content;
private Date pubDate;
public Message() {
}
public Integer getId() {
return this.id;
}
public String getContent() {
return this.content;
}
public Date getPubDate() {
return this.pubDate;
}
public void setId(Integer id) {
this.id = id;
}
public void setContent(String content) {
this.content = content;
}
public void setPubDate(Date pubDate) {
this.pubDate = pubDate;
}
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (!(o instanceof Message)) {
return false;
} else {
Message other = (Message)o;
if (!other.canEqual(this)) {
return false;
} else {
label47: {
Object this$id = this.getId();
Object other$id = other.getId();
if (this$id == null) {
if (other$id == null) {
break label47;
}
} else if (this$id.equals(other$id)) {
break label47;
}
return false;
}
Object this$content = this.getContent();
Object other$content = other.getContent();
if (this$content == null) {
if (other$content != null) {
return false;
}
} else if (!this$content.equals(other$content)) {
return false;
}
Object this$pubDate = this.getPubDate();
Object other$pubDate = other.getPubDate();
if (this$pubDate == null) {
if (other$pubDate != null) {
return false;
}
} else if (!this$pubDate.equals(other$pubDate)) {
return false;
}
return true;
}
}
}
protected boolean canEqual(Object other) {
return other instanceof Message;
}
public int hashCode() {
int PRIME = true;
int result = 1;
Object $id = this.getId();
int result = result * 59 + ($id == null ? 43 : $id.hashCode());
Object $content = this.getContent();
result = result * 59 + ($content == null ? 43 : $content.hashCode());
Object $pubDate = this.getPubDate();
result = result * 59 + ($pubDate == null ? 43 : $pubDate.hashCode());
return result;
}
public String toString() {
Integer var10000 = this.getId();
return "Message(id=" + var10000 + ", content=" + this.getContent() + ", pubDate=" + this.getPubDate() + ")";
}
}
通过上面代码发现,只要在类上声明了@Data注解,就可以根据当前类所定义的属性来生成相应的setter、getter、toString()、equals()、hashCode()方法,值得注意的是,@Data注解并不会生成构造方法。
2、@NonNull注解
@NonNull使用在属性上,被其注解的属性不许为空。
import lombok.Data;
import lombok.NonNull;
import java.util.Date;
@Data
public class Message {
@NonNull
private Integer id;
@NonNull
private String content;
private Date pubDate;
}
通过反编译工具查看生成的.class
文件:
public class Message {
@NonNull
private Integer id;
@NonNull
private String content;
private Date pubDate;
public Message(@NonNull Integer id, @NonNull String content) {
if (id == null) {
throw new NullPointerException("id is marked non-null but is null");
} else if (content == null) {
throw new NullPointerException("content is marked non-null but is null");
} else {
this.id = id;
this.content = content;
}
}
@NonNull
public Integer getId() {
return this.id;
}
@NonNull
public String getContent() {
return this.content;
}
public Date getPubDate() {
return this.pubDate;
}
public void setId(@NonNull Integer id) {
if (id == null) {
throw new NullPointerException("id is marked non-null but is null");
} else {
this.id = id;
}
}
public void setContent(@NonNull String content) {
if (content == null) {
throw new NullPointerException("content is marked non-null but is null");
} else {
this.content = content;
}
}
public void setPubDate(Date pubDate) {
this.pubDate = pubDate;
}
// equals、canEqual、hashCode、toString省略粘贴
}
此时发现反编译后生成了有参构造,构造方法的参数是@NonNull注解标注的属性,在构造方法和@NonNull注解标注的属性的set方法中都对属性进行了null
校验。由于类中已经有了构造方法,那么便不会自动生成无参构造,如果想要生成无参构造,则需要在类上使用@NoArgsConstructor
注解,但是使用无参构造生成对象则违背了@NonNull注解属性不为空的原则。
3、@RequiredArgsConstructor注解
在类上标注@RequiredArgsConstructor注解
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import java.util.Date;
@Data
@NoArgsConstructor
@RequiredArgsConstructor(staticName = "setNonNullField")
public class Message {
@NonNull
private Integer id;
@NonNull
private String content;
private Date pubDate;
}
通过反编译工具查看生成的.class
文件:
private Message(@NonNull Integer id, @NonNull String content) {
if (id == null) {
throw new NullPointerException("id is marked non-null but is null");
} else if (content == null) {
throw new NullPointerException("content is marked non-null but is null");
} else {
this.id = id;
this.content = content;
}
}
public static Message setNonNullField(@NonNull Integer id, @NonNull String content) {
return new Message(id, content);
}
@RequiredArgsConstructor
注解可以生成@NonNul
l注解所标注属性的私有构造,并且指定了static
方法调用该私有构造构建对象。
4、@Builder注解
@Builder注解可以生成构建者模式代码。
import lombok.*;
import java.util.Date;
@Data
@Builder
public class Message {
private Integer id;
private String content;
private Date pubDate;
}
通过反编译工具查看生成的.class
文件:
import java.util.Date;
public class Message {
private Integer id;
private String content;
private Date pubDate;
Message(Integer id, String content, Date pubDate) {
this.id = id;
this.content = content;
this.pubDate = pubDate;
}
public static Message.MessageBuilder builder() {
return new Message.MessageBuilder();
}
// 其他方法省略...
public static class MessageBuilder {
private Integer id;
private String content;
private Date pubDate;
MessageBuilder() {
}
public Message.MessageBuilder id(final Integer id) {
this.id = id;
return this;
}
public Message.MessageBuilder content(final String content) {
this.content = content;
return this;
}
public Message.MessageBuilder pubDate(final Date pubDate) {
this.pubDate = pubDate;
return this;
}
public Message build() {
return new Message(this.id, this.content, this.pubDate);
}
public String toString() {
return "Message.MessageBuilder(id=" + this.id + ", content=" + this.content + ", pubDate=" + this.pubDate + ")";
}
}
}
5、@Accessors注解
@Accessors
注解可以生成访问器模式代码,同时对于访问器操作形式提供了三种不同的方案:fluent、chain、prefix。
5.1 fluent模式
import lombok.*;
import lombok.experimental.Accessors;
import java.util.Date;
@Data
@Accessors(fluent = true)
public class Message {
private Integer id;
private String content;
private Date pubDate;
}
通过反编译工具查看生成的.class
文件:
import java.util.Date;
public class Message {
private Integer id;
private String content;
private Date pubDate;
public Message() {
}
public Integer id() {
return this.id;
}
public String content() {
return this.content;
}
public Date pubDate() {
return this.pubDate;
}
public Message id(Integer id) {
this.id = id;
return this;
}
public Message content(String content) {
this.content = content;
return this;
}
public Message pubDate(Date pubDate) {
this.pubDate = pubDate;
return this;
}
// 其他方法省略...
}
通过以上代码发现,设置为fluent模式后属性设置方法和属性获取方法的方法名全都变成了属性名,这样可以直接通过代码链的形式进行设置。
5.2 chain模式
import lombok.*;
import lombok.experimental.Accessors;
import java.util.Date;
@Data
@Accessors(chain = true)
public class Message {
private Integer id;
private String content;
private Date pubDate;
}
通过反编译工具查看生成的.class
文件:
import java.util.Date;
public class Message {
private Integer id;
private String content;
private Date pubDate;
public Message() {
}
public Integer getId() {
return this.id;
}
public String getContent() {
return this.content;
}
public Date getPubDate() {
return this.pubDate;
}
public Message setId(final Integer id) {
this.id = id;
return this;
}
public Message setContent(final String content) {
this.content = content;
return this;
}
public Message setPubDate(final Date pubDate) {
this.pubDate = pubDate;
return this;
}
// 其他方法省略...
}
使用chain模式后发现生成了setter、getter方法,但是setter方法的返回值不再是void
而是this
,这样在属性设置时可以直接使用代码链的方式。
5.3 prefix模式
import lombok.*;
import lombok.experimental.Accessors;
import java.util.Date;
@Data
@Accessors(prefix = "it")
public class Message {
private Integer itId;
private String itContent;
private Date itPubDate;
}
通过反编译工具查看生成的.class
文件:
import java.util.Date;
public class Message {
private Integer itId;
private String itContent;
private Date itPubDate;
public Message() {
}
public Integer getId() {
return this.itId;
}
public String getContent() {
return this.itContent;
}
public Date getPubDate() {
return this.itPubDate;
}
public void setId(final Integer itId) {
this.itId = itId;
}
public void setContent(final String itContent) {
this.itContent = itContent;
}
public void setPubDate(final Date itPubDate) {
this.itPubDate = itPubDate;
}
// 其他方法省略...
}
使用chain模式后可以在setter、getter方法上去掉声明的属性前缀。
6、@Cleanup注解与@SneakyThrows注解
@Cleanup可以实现资源的自动关闭,@SneakyThrows注解会帮助程序手工的处理异常,二者经常配合使用。
import lombok.Cleanup;
import lombok.Data;
import lombok.NonNull;
import lombok.SneakyThrows;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
@Data
public class MessageRead { // 此时程序会生成双参构造
@NonNull
private String filePath; // 文件路径
@NonNull
private String fileName; // 文件名称
@SneakyThrows //帮助用户手工处理异常
public String load() { // 文件读取
@Cleanup InputStream input = new FileInputStream(new File(filePath, fileName));
byte[] data = new byte[1024];
int len = input.read(data);
return new String(data, 0, len);
}
}
通过反编译工具查看生成的.class
文件:
import java.io.File;
import java.io.FileInputStream;
import java.util.Collections;
import lombok.NonNull;
public class MessageRead {
@NonNull
private String filePath;
@NonNull
private String fileName;
public String load() {
try {
FileInputStream input = new FileInputStream(new File(this.filePath, this.fileName));
String var4;
try {
byte[] data = new byte[1024];
int len = input.read(data);
var4 = new String(data, 0, len);
} finally {
if (Collections.singletonList(input).get(0) != null) {
input.close();
}
}
return var4;
} catch (Throwable var9) {
throw var9;
}
}
// 其他方法省略...
}
通过以上编译后的.class
文件发现,@SneakyThrows注解声名的方法编译后会生成try-catch
代码块,@Cleanup注解则会生成try-finally
代码块并在finally
中关闭资源。
7、@Synchronized注解
@Synchronized注解声明在方法上可以自动生成synchronized
代码块,synchronized
代码块锁定的同步对象是lombok自动生成的Object数组。
import lombok.SneakyThrows;
import lombok.Synchronized;
public class TicketShop {
private Integer ticket = 10;
@SneakyThrows
@Synchronized
public void sale() {
while (ticket > 0) {
if (ticket > 0) {
System.out.println("[" + Thread.currentThread().getName() + "] 剩余: " + ticket--);
}
}
}
}
通过反编译工具查看生成的.class
文件:
import java.io.PrintStream;
public class TicketShop {
private final Object $lock = new Object[0];
private Integer ticket = 10;
public TicketShop() {
}
public void sale() {
synchronized(this.$lock) {
try {
while(this.ticket > 0) {
if (this.ticket > 0) {
PrintStream var10000 = System.out;
String var10001 = Thread.currentThread().getName();
Integer var2 = this.ticket;
Integer var3 = this.ticket = this.ticket - 1;
var10000.println("[" + var10001 + "] 剩余: " + var2);
}
}
} catch (Throwable var5) {
throw var5;
}
}
}
}
虽然@Synchronized注解可以自动添加synchronized
代码块,但是会存在死锁的隐患,不推荐使用在真实项目中。