( 四 )、Springboot使用 Lombok
( 四 )、Springboot使用 Lombok
1、简介
自从Java 6起,javac就支持“JSR 269 Pluggable Annotation Processing API”规范,只要程序实现了该API,就能在javac运行的时候得到调用。
Lombok就是一个实现了"JSR 269 API"的程序。在使用javac的过程中,它产生作用的具体流程如下:
1. javac对源代码进行分析,生成一棵抽象语法树(AST)。
2. javac编译过程中调用实现了JSR 269的Lombok程序。
3. 此时Lombok就对第一步骤得到的AST进行处理,找到Lombok注解所在类对应的语法树 (AST),然后修改该语法树(AST),增加Lombok注解定义的相应树节点。
4. javac使用修改后的抽象语法树(AST)生成字节码文件。
2、使用
2.1、idea安装lombok插件
2.2、重启idea
2.3、添加maven依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
<scope>provided</scope>
</dependency>
四、lombok 注解介绍
@Getter 、@Setter: 作用类上,生成所有成员变量的getter/setter方法;作用于成员变量上,生成该成员变量的getter/setter方法。
@ToString:作用于类,覆盖默认的toString()方法,可以通过of属性限定显示某些字段,通过exclude属性排除某些字段。
@EqualsAndHashCode:作用于类,覆盖默认的equals和hashCode。
@NonNull:主要作用于成员变量和参数中,标识不能为空,否则抛出空指针异常。
@NoArgsConstructor:生成无参构造器;
@RequiredArgsConstructor:生成包含final和@NonNull注解的成员变量的构造器;
@AllArgsConstructor:生成全参构造器
@Builder:作用于类上,将类转变为建造者模式
@Log:作用于类上,生成日志变量。针对不同的日志实现产品,有不同的注解。
五、使用示例
1、@Getter/@Setter
1.1、此注解在属性或类上,可以为相应的属性自动生成 Getter/Setter 方法,还可以指定访问范围
public class User1 {
@Getter @Setter
private Long id;
@Getter(AccessLevel.PROTECTED)
private String phone;
private String password;
}
编译结果:
public class User1 {
private Long id;
private String phone;
private String password;
public User1() {
}
public Long getId() {
return this.id;
}
public void setId(final Long id) {
this.id = id;
}
protected String getPhone() {
return this.phone;
}
}
1.2、注解在类上,表示为类中的所有字段生成Getter&Setter方法
@Getter
@Setter
public class User1 {
private Long id;
@Getter(AccessLevel.PROTECTED)
private String phone;
@Setter(AccessLevel.NONE)
private String password;
}
编译后:
public class User1 {
private Long id;
private String phone;
private String password;
public User1() {
}
public Long getId() {
return this.id;
}
public String getPassword() {
return this.password;
}
public void setId(final Long id) {
this.id = id;
}
public void setPhone(final String phone) {
this.phone = phone;
}
protected String getPhone() {
return this.phone;
}
}
@Getter(lazy = true)
标注字段为懒加载字段,懒加载字段在创建对象时不会进行初始化,而是在第一次访问的时候才会初始化,后面再次访问也不会重复初始化
2、@ToString
类使用此注解,生成toString()方法,默认情况下它会按顺序(以逗号分隔)打印你的类名称以及每个字段。可以这样设置不包含哪些字段,可以指定一个也可以指定多个@ToString(exclude = “id”) / @ToString(exclude = {“id”,“name”})
@ToString(of = {"s"})
public class User1 {
static String s = "";
private Long id;
private String phone;
private String password;
private String salt;
}
编译后:
public class User1 {
static String s = "";
private Long id;
private String phone;
private String password;
private String salt;
public User1() {
}
public String toString() {
return "User1(s=" + s + ")";
}
}
如果继承的有父类的话,可以设置callSuper 让其调用父类的toString()方法,例如:@ToString(callSuper = true)
public class superUser {
private String phone;
}
@ToString(callSuper = true)
public class User1 extends superUser {
static String s = "";
private Long id;
private String password;
private String salt;
}
编译后:
public class User1 extends superUser {
static String s = "";
private Long id;
private String password;
private String salt;
public User1() {
}
public String toString() {
String var10000 = super.toString();
return "User1(super=" + var10000 + ", id=" + this.id + ", password=" + this.password + ", salt=" + this.salt + ")";
}
}
3、@EqualsAndHashCode
通过判断两个对象的成员变量值是否相等,来判断这两个对象是否相等,要重写hashCode()和equals()方法。
用在类上,生成hashCode()和equals()方法,默认情况下,它将使用所有非静态,非transient字段。但可以通过在可选的 exclude 参数中来排除更多字段。或者,通过在of参数中命名它们来准确指定希望使用哪些字段。
@EqualsAndHashCode
public class User implements Serializable{
private static final long serialVersionUID = 6569081236403751407L;
private Long id;
private String phone;
private transient int status;
}
编译后:
public class User implements Serializable {
private static final long serialVersionUID = 6569081236403751407L;
private Long id;
private String phone;
private transient int status;
public User() {
}
public boolean equals(Object o) {
// 判断两个对象是不是同一个对象
if(o == this) {
return true;
// 判断 o 是不是 User 的一个实例
} else if(!(o instanceof User)) {
return false;
} else {
User other = (User)o;
// 判断两个对象是否可以比较
if(!other.canEqual(this)) {
return false;
} else {
Long this$id = this.id;
Long other$id = other.id;
if(this$id == null) {
if(other$id != null) {
return false;
}
} else if(!this$id.equals(other$id)) {
return false;
}
String this$phone = this.phone;
String other$phone = other.phone;
if(this$phone == null) {
if(other$phone != null) {
return false;
}
} else if(!this$phone.equals(other$phone)) {
return false;
}
return true;
}
}
}
/**
* 判断这个对象是不是 User 的实例
*/
protected boolean canEqual(Object other) {
return other instanceof User;
}
public int hashCode() {
boolean PRIME = true;
byte result = 1;
Long $id = this.id;
int result1 = result * 59 + ($id == null?43:$id.hashCode());
String $phone = this.phone;
result1 = result1 * 59 + ($phone == null?43:$phone.hashCode());
return result1;
}
}
4、@Data
@Data 注解在类上, 相当于同时使用了@ToString、@EqualsAndHashCode、@Getter、@Setter和@RequiredArgsConstrutor这些注解,会为类的所有属性自动生成 getter/setter,equals,canEqual,hashCode,toString 方法,如为 final 属性,则不会生成 setter 方法
@Data
public class User {
/**
* @Data 只对成员变量起作用,
*/
static String s = "";
/**
* 由于是final修饰的成员变量,不可更改,只会生成get,不会有set
*/
final int id2;
@NonNull
private Integer id;
private String userName;
}
5、@NonNull
用在属性或构造器上,为字段赋值时(即调用字段的setter方法时),如果传的参数为null,则会抛出空异常NullPointerException,生成setter方法时会对参数是否为空检查
public class NonNullExample extends Something {
private String name;
public NonNullExample(@NonNull Person person) {
super("Hello");
this.name = person.getName();
}
}
编译后:
public class NonNullExample extends Something {
private String name;
public NonNullExample(@NonNull Person person) {
super("Hello");
if (person == null) {
throw new NullPointerException("person");
}
this.name = person.getName();
}
}
6、@NoArgsConstructor
生成一个无参构造方法。当类中有final字段没有被初始化时,编译器会报错,此时可用@NoArgsConstructor(force = true),然后就会为没有初始化的final字段设置默认值 0 / false / null, 这样编译器就不会报错。对于具有约束的字段(例如@NonNull字段),不会生成检查或分配,因此请注意,正确初始化这些字段之前,这些约束无效。
@NoArgsConstructor(force = true)
public class User {
private Long id;
@NonNull
private String phone;
private final Integer age;
}
编译后:
public class User {
private Long id;
@NonNull
private String phone;
private final Integer age = null;
public User() {
}
}
7、@RequiredArgsConstructor
对指定的参数生成构造方法。生成构造方法(可能带参数也可能不带参数),如果带参数,这参数只能是以final修饰的未经初始化的字段(若用final修饰了,就不会再改变,生成构造方法就没意义),或者是以@NonNull注解的未经初始化的字段。
@RequiredArgsConstructor
public class User1 {
private Long id;
@NonNull
private String phone;
@NotNull
private Integer status = 0;
private final Integer age;
private final String country = "china";
}
编译后:
public class User1 {
private Long id;
@NonNull
private String phone;
@NotNull
private Integer status = 0;
private final Integer age;
private final String country = "china";
public User1(@NonNull final String phone, final Integer age) {
if (phone == null) {
throw new NullPointerException("phone is marked non-null but is null");
} else {
this.phone = phone;
this.age = age;
}
}
}
@RequiredArgsConstructor(staticName = “of”) 会生成一个 of() 的静态方法,并把构造方法设置为私有的
@RequiredArgsConstructor(staticName = "of")
public class User1 {
private Long id;
@NonNull
private String phone;
@NotNull
private Integer status = 0;
private final Integer age;
private final String country = "china";
}
编译后:
public class User1 {
private Long id;
@NonNull
private String phone;
@NotNull
private Integer status = 0;
private final Integer age;
private final String country = "china";
private User1(@NonNull final String phone, final Integer age) {
if (phone == null) {
throw new NullPointerException("phone is marked non-null but is null");
} else {
this.phone = phone;
this.age = age;
}
}
public static User1 of(@NonNull final String phone, final Integer age) {
return new User1(phone, age);
}
}
8、@AllArgsConstructor
用在类上,生成一个全参数的构造方法
@AllArgsConstructor
public class User {
private Long id;
@NonNull
private String phone;
@NotNull
private Integer status = 0;
private final Integer age;
private final String country = "china";
}
编译后:
public class User1 {
private Long id;
@NonNull
private String phone;
@NotNull
private Integer status = 0;
private final Integer age;
private final String country = "china";
public User1(final Long id, @NonNull final String phone, final Integer status, final Integer age) {
if (phone == null) {
throw new NullPointerException("phone is marked non-null but is null");
} else {
this.id = id;
this.phone = phone;
this.status = status;
this.age = age;
}
}
}
9、@Value
用在类上,是@Data的不可变形式,相当于为属性添加final声明,只提供getter方法,而不提供setter方法
@Value
public class LombokDemo{
@NonNull
private int id;
}
编译后:
publicclassLombokDemo {
private final int id;
public int getId() {
return this.id;
}
}
10、@Builder
Builder 使用创建者模式又叫生成器模式(Builder Pattern)。简单来说,就是一步步创建一个对象,它对用户屏蔽了里面构建的细节,但却可以精细地控制对象的构造过程。
原理:
public class User1 {
private Integer id;
private String name;
User1(final Integer id, final String name) {
this.id = id;
this.name = name;
}
// 在实体类中:会创建一个builder()方法,它的目的是用来创建构建器。
public static User1.User1Builder builder() {
return new User1.User1Builder();
}
// 内部静态类,具有和实体类相同的属性(成为构建器)
public static class User1Builder {
// 在构建器中:对于目标类中的所有的属性和未初始化的final字段,都会在构建器中创建对应属性。
private Integer id;
private String name;
// 创建一个无参的default构造函数。
User1Builder() {
}
// 对于实体类中的每个参数,都会对应创建类似于setter的方法,只不过方法名与该参数名相同。
// 并且返回值是构建器本身(便于链式调用)
public User1.User1Builder id(final Integer id) {
this.id = id;
return this;
}
public User1.User1Builder name(final String name) {
this.name = name;
return this;
}
// 调用此方法,就会根据设置的值进行创建实体对象
public User1 build() {
return new User1(this.id, this.name);
}
public String toString() {
return "User1.User1Builder(id=" + this.id + ", name=" + this.name + ")";
}
}
}
常规用法:
@Data
public class User1 {
private Integer id;
private String name;
private String address;
public static void main(String[] args) {
User1 user1 = new User1(1, "java", "china");
user1.setId(1);
user1.setName("11");
user1.setAddress("111");
System.out.println(user1);
}
}
使用 @Builder:
@Builder
@Data
public class User1 {
private Integer id;
private String name;
private String address;
public static void main(String[] args) {
User1 user1 = User1.builder().id(1).address("11").name("111").build();
System.out.println(user1);
}
}