[TOC]
在开发过程中,通常都会定义大量的JavaBean,然后通过IDE去产生其属性的构造器、getter、setter、equals、hashcode、toString方法,当要增加属性或者对某个属性进行改变时,比如命名、类型等,都需要重新去产生上面提到的这些方法。这样重复的劳动没有任何意义,Lombok里面的注解可以轻松解决这些问题。
lombok实现的原理:主要是通过抽象语法树(AST),在编译处理后,对应到有其注解的类,那么注解编译器就会自动去对应项目中的注解对应到在lombok语法树中的注解文件,并经过自动编译对应来产生对应类中的getter或者setter方法,达到简化代码的目的
pom.xml添加lombok
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
@Getter @Setter注解
这一对注解从名字上就很好理解,用在成员变量前面,相当于为成员变量生成对应的get和set方法,同时还可以为生成的方法指定访问修饰符,当然,默认为public,直接来看下面的简单的例子:
// Rumenz.java
/**
* @className: Rumenz
* @description: TODO 类描述
* @author: 入门小站 rumenz.com
* @date: 2021/12/9
**/
public class RumenzGetSet {
@Getter @Setter
private Integer id;
@Getter @Setter
private String name;
}
- 等价于
public class RumenzGetSet {
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@RestController
@RequestMapping("/rumenz")
public class RumenzController {
@GetMapping("/index")
public String index(){
RumenzGetSet r=new RumenzGetSet();
r.setId(1);
r.setName("入门小站");
return r.getId()+r.getName();
}
}
访问
http://127.0.0.1:8080/rumenz/index
返回1入门小站
@NonNull注解
这个注解可以用在成员方法或者构造方法的参数前面,会自动产生一个关于此参数的非空检查,如果参数为空,则抛出一个空指针异常,举个例子来看看
public class RumenzNonNull {
@Getter @Setter @NonNull
private Integer id;
@Getter @Setter @NonNull
private String name;
}
- 等价于
public class RumenzNonNull {
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
if (id == null) throw new java.lang.NullPointerException("id");
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
if (name == null) throw new java.lang.NullPointerException("name");
this.name = name;
}
}
@GetMapping("/index1")
public String index1(){
RumenzNonNull r=new RumenzNonNull();
r.setId(1);
r.setName(null);
return r.getId()+r.getName();
}
访问
http://127.0.0.1:8080/rumenz/index1
报错java.lang.NullPointerException: name is marked non-null but is null
@ToString
@ToString
public class RumenzToString {
@Getter @Setter
private Integer id;
@Getter @Setter
private String name;
@Override
public String toString() {
return "RumenzToString{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
- 等价于
public class RumenzToString {
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "RumenzToString{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
@GetMapping("/index2")
public String index2(){
RumenzToString r=new RumenzToString();
r.setId(1);
r.setName("入门小站");
return r.toString();
}
访问
http://127.0.0.1:8080/rumenz/index1
返回RumenzToString{id=1, name='入门小站'}
EqualsAndHashCode注解
@EqualsAndHashCode
public class RumenzEqualsAndHashCode {
@Getter @Setter
private Integer id;
@Getter @Setter
private String name;
}
- 等价于
public class RumenzEqualsAndHashCode {
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof RumenzEqualsAndHashCode)) return false;
RumenzEqualsAndHashCode that = (RumenzEqualsAndHashCode) o;
return id.equals(that.id) &&
name.equals(that.name);
}
}
@GetMapping("/index3")
public String index3(){
RumenzEqualsAndHashCode r1=new RumenzEqualsAndHashCode();
r1.setId(1);
r1.setName("入门小站");
RumenzEqualsAndHashCode r2=new RumenzEqualsAndHashCode();
r2.setId(1);
r2.setName("入门小站");
if(r1.equals(r2)){
return "相等";
}
return "不相等";
}
@Data注解
- 1)生成无参构造方法;
- 2)属性的set/get方法;
- 3)equals(), hashCode(), toString(), canEqual()方法。
@Data(staticConstructor="of")
public class RumenzData {
private Integer id;
private String name;
}
//等价于
public class RumenzData {
private Integer id;
private String name;
private RumenzData() {
}
public static RumenzData of() {
return new RumenzData();
}
public Integer getId() {
return this.id;
}
public String getName() {
return this.name;
}
public void setId(final Integer id) {
this.id = id;
}
public void setName(final String name) {
this.name = name;
}
public boolean equals(final Object o) {
if (o == this) {
return true;
} else if (!(o instanceof RumenzData)) {
return false;
} else {
RumenzData other = (RumenzData)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;
}
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;
}
}
}
protected boolean canEqual(final Object other) {
return other instanceof RumenzData;
}
public int hashCode() {
int PRIME = true;
int result = 1;
Object $id = this.getId();
int result = result * 59 + ($id == null ? 43 : $id.hashCode());
Object $name = this.getName();
result = result * 59 + ($name == null ? 43 : $name.hashCode());
return result;
}
public String toString() {
return "RumenzData(id=" + this.getId() + ", name=" + this.getName() + ")";
}
}
@GetMapping("/index4")
public String index4(){
RumenzData of = RumenzData.of();
of.setName("入门小站");
String name = of.getName();
return name;
}
@Cleanup注解
这个注解用在变量前面,可以保证此变量代表的资源会被自动关闭,默认是调用资源的close()方法,如果该资源有其它关闭方法,可使用@Cleanup(“methodName”)来指定要调用的方法,就用输入输出流来举个例子吧:
@GetMapping("/index5")
public String index5() throws IOException {
File file = ResourceUtils.getFile("classpath:application.properties");
@Cleanup InputStream inputStream = new FileInputStream(file);
byte b[]=new byte[(int) file.length()];
inputStream.read(b);
//@Cleanup 代替了 inputStream.close();
return new String(b);
}
等价于
@GetMapping({"/index5"})
public String index5() throws IOException {
File file = ResourceUtils.getFile("classpath:application.properties");
FileInputStream inputStream = new FileInputStream(file);
String var4;
try {
byte[] b = new byte[(int)file.length()];
inputStream.read(b);
var4 = new String(b);
} finally {
if (Collections.singletonList(inputStream).get(0) != null) {
inputStream.close();
}
}
return var4;
}
@NoArgsConstructor注解
@NoArgsConstructor在类上使用,它可以提供一个无参构造器
@NoArgsConstructor
public class RumenzNoArgsConstructor {
private Integer id;
private String name;
}
等价于
public class RumenzNoArgsConstructor {
private Integer id;
private String name;
public RumenzNoArgsConstructor() {
}
}
@RequiredArgsConstructor注解
指定final的属性生成构造方法
@ToString
@RequiredArgsConstructor
public class RumenzRequiredArgsConstructor {
private Integer id;
private final String name;
}
// 等价于
public class RumenzRequiredArgsConstructor {
private Integer id;
private final String name; //final
public String toString() {
return "RumenzRequiredArgsConstructor(id=" + this.id + ", name=" + this.name + ")";
}
public RumenzRequiredArgsConstructor(final String name) {
this.name = name;
}
}
@AllArgsConstructor注解
类中所有的字段都生成一个有参的构造方法.
@ToString
@AllArgsConstructor
public class RumenzAllArgsConstructor {
private Integer id;
private String name;
}
//等价于
public class RumenzAllArgsConstructor {
private Integer id;
private String name;
public String toString() {
return "RumenzAllArgsConstructor(id=" + this.id + ", name=" + this.name + ")";
}
public RumenzAllArgsConstructor(final Integer id, final String name) {
this.id = id;
this.name = name;
}
}
@Value注解
- 1)有参构造方法;
- 2)只添加@Value注解,没有其他限制,那么类属性会被编译成final的,因此只有get方法,而没有set方法。
@ToString
@Value
public class RumenzValue {
private Integer id;
private String name;
}
// 等价于
public final class RumenzValue {
private final Integer id;
private final String name;
public RumenzValue(final Integer id, final String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return this.id;
}
public String getName() {
return this.name;
}
public boolean equals(final Object o) {
if (o == this) {
return true;
} else if (!(o instanceof RumenzValue)) {
return false;
} else {
RumenzValue other = (RumenzValue)o;
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;
}
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;
Object $id = this.getId();
int result = result * 59 + ($id == null ? 43 : $id.hashCode());
Object $name = this.getName();
result = result * 59 + ($name == null ? 43 : $name.hashCode());
return result;
}
public String toString() {
return "RumenzValue(id=" + this.getId() + ", name=" + this.getName() + ")";
}
}
@SneakyThrows注解
这个注解用在方法上,可以将方法中的代码用try-catch语句包裹起来,捕获异常并在catch中用Lombok.sneakyThrow(e)把异常抛出,可以使用@SneakyThrows(Exception.class)的形式指定抛出哪种异常,很简单的注解,直接看个例子:
@SneakyThrows
@GetMapping("/index9")
public String index9() {
//使用@SneakyThrows就不用显式抛出异常了
File file = ResourceUtils.getFile("classpath:application.properties");
@Cleanup InputStream inputStream = new FileInputStream(file);
byte b[]=new byte[(int) file.length()];
inputStream.read(b);
//@Cleanup 代替了 inputStream.close();
return new String(b);
}
// 等价于
@GetMapping({"/index9"})
public String index9() {
try {
File file = ResourceUtils.getFile("classpath:application.properties");
FileInputStream inputStream = new FileInputStream(file);
String var4;
try {
byte[] b = new byte[(int)file.length()];
inputStream.read(b);
var4 = new String(b);
} finally {
if (Collections.singletonList(inputStream).get(0) != null) {
inputStream.close();
}
}
return var4;
} catch (Throwable var9) {
throw var9;
}
}
@Synchronized注解
synchronized是线程安全中一个重要的关键字,它是一种同步锁,主要用来保证在同一个时刻,只有一个线程可以执行某个方法或者某段代码块。一般使用synchronized去锁住代码块,而不是方法,因为锁住代码块效率更高。
public class RumenzSynchronized {
private final Object readLock = new Object();
@Synchronized
public static void hello() {
System.out.println("rumenz.com");
}
@Synchronized
public int answerToLife() {
return 110;
}
@Synchronized("readLock")
public void foo() {
System.out.println("入门小站");
}
}
// 等价于
public class RumenzSynchronized {
private static final Object $LOCK = new Object[0];
private final Object $lock = new Object[0];
private final Object readLock = new Object();
public RumenzSynchronized() {
}
public static void hello() {
synchronized($LOCK) {
System.out.println("rumenz.com");
}
}
public int answerToLife() {
synchronized(this.$lock) {
return 110;
}
}
public void foo() {
synchronized(this.readLock) {
System.out.println("入门小站");
}
}
}
@Builder注解
用在类、构造器、方法上,为你提供复杂的builder APIs
@ToString
@Builder
public class RumenzBuilder {
private Integer id;
private String name;
}
// 等价于
public class RumenzBuilder {
private Integer id;
private String name;
RumenzBuilder(final Integer id, final String name) {
this.id = id;
this.name = name;
}
public static RumenzBuilder.RumenzBuilderBuilder builder() {
return new RumenzBuilder.RumenzBuilderBuilder();
}
public String toString() {
return "RumenzBuilder(id=" + this.id + ", name=" + this.name + ")";
}
public static class RumenzBuilderBuilder {
private Integer id;
private String name;
RumenzBuilderBuilder() {
}
public RumenzBuilder.RumenzBuilderBuilder id(final Integer id) {
this.id = id;
return this;
}
public RumenzBuilder.RumenzBuilderBuilder name(final String name) {
this.name = name;
return this;
}
public RumenzBuilder build() {
return new RumenzBuilder(this.id, this.name);
}
public String toString() {
return "RumenzBuilder.RumenzBuilderBuilder(id=" + this.id + ", name=" + this.name + ")";
}
}
}
@GetMapping("/index11")
public String index11() {
RumenzBuilder rb=RumenzBuilder.builder().id(1).name("入门小站").build();
return rb.toString();
}
@SuperBuilder
当实体类有集成关系时,需要用@SuperBuilder,否则调用的.builder都会报错.
@Builder并不支持父类成员属性的构造,@SuperBuilder注解的出现,就是用来解决这个问题。
使用@Builder或@SuperBuilder注解时,不会默认创建空参构造函数,如果你有额外使用空参构造函数或全参构造函数的需求,需要在子类和父类都加上以下注解:
本小结源码地址:
介绍
- 我的博客 https://rumenz.com/ ,
- 我的工具箱 https://tooltt.com/