目录
二、官网:http://projectlombok.org/ ;
一、lombok介绍:
就是通过@Data注解的方式省去了我们平时开发定义JavaBean之后,生成其属性的构造器、getter、setter、equals、hashcode、toString方法;但是,在编译时会自动生成这些方法,在.class文件中。
二、官网:http://projectlombok.org/ ;
文档地址:http://projectlombok.org/features/index.
三、安装:
打开 IDEA 的 Settings 面板,并选择 Plugins 选项,然后点击 “Browse repositories”
在输入框输入”lombok”,得到搜索结果,点击安装,然后安装提示重启 IDEA,安装成功;
四、引入依赖:
maven项目:在自己的项目里添加 lombok 的编译支持,在 pom 文件里面添加 dependency
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
<scope>provided</scope>
</dependency>
非maven项目:首先下载lombok.jar包:https://projectlombok.org/download.html
具体引用方式不做详细讲解,跟其他jar引入方式一样
五、有哪些注解:
- @Data
- @Setter
- @Getter
- @Log4j
- @Slf4j
- @AllArgsConstructor
- @NoArgsConstructor
- @EqualsAndHashCode
- @NonNull
- @Cleanup
- @ToString
- @RequiredArgsConstructor
- @Value
- @SneakyThrows
- @Synchronized
- @Accessors(chain = true)
- 还有一些注解 @builder @Singular @Delegate @Accessors @Wither @val @UtilityClass
等就不在做详细说明 有需要的可以自行百度哈
六、注解详细说明:
@Data
注解在 类 上;提供类所有属性的 get 和 set 方法,此外还提供了equals、canEqual、hashCode、toString 方法。
官方实例:
import lombok.AccessLevel;
import lombok.Setter;
import lombok.Data;
import lombok.ToString;
@Data public class DataExample {
private final String name;
@Setter(AccessLevel.PACKAGE) private int age;
private double score;
private String[] tags;
@ToString(includeFieldNames=true)
@Data(staticConstructor="of")
public static class Exercise<T> {
private final String name;
private final T value;
}
}
使用Lombok,则实现如下:
import java.util.Arrays;
public class DataExample {
private final String name;
private int age;
private double score;
private String[] tags;
public DataExample(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
void setAge(int age) {
this.age = age;
}
public int getAge() {
return this.age;
}
public void setScore(double score) {
this.score = score;
}
public double getScore() {
return this.score;
}
public String[] getTags() {
return this.tags;
}
public void setTags(String[] tags) {
this.tags = tags;
}
@Override public String toString() {
return "DataExample(" + this.getName() + ", " + this.getAge() + ", " + this.getScore() + ", " + Arrays.deepToString(this.getTags()) + ")";
}
protected boolean canEqual(Object other) {
return other instanceof DataExample;
}
@Override public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof DataExample)) return false;
DataExample other = (DataExample) o;
if (!other.canEqual((Object)this)) return false;
if (this.getName() == null ? other.getName() != null : !this.getName().equals(other.getName())) return false;
if (this.getAge() != other.getAge()) return false;
if (Double.compare(this.getScore(), other.getScore()) != 0) return false;
if (!Arrays.deepEquals(this.getTags(), other.getTags())) return false;
return true;
}
@Override public int hashCode() {
final int PRIME = 59;
int result = 1;
final long temp1 = Double.doubleToLongBits(this.getScore());
result = (result*PRIME) + (this.getName() == null ? 43 : this.getName().hashCode());
result = (result*PRIME) + this.getAge();
result = (result*PRIME) + (int)(temp1 ^ (temp1 >>> 32));
result = (result*PRIME) + Arrays.deepHashCode(this.getTags());
return result;
}
public static class Exercise<T> {
private final String name;
private final T value;
private Exercise(String name, T value) {
this.name = name;
this.value = value;
}
public static <T> Exercise<T> of(String name, T value) {
return new Exercise<T>(name, value);
}
public String getName() {
return this.name;
}
public T getValue() {
return this.value;
}
@Override public String toString() {
return "Exercise(name=" + this.getName() + ", value=" + this.getValue() + ")";
}
protected boolean canEqual(Object other) {
return other instanceof Exercise;
}
@Override public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Exercise)) return false;
Exercise<?> other = (Exercise<?>) o;
if (!other.canEqual((Object)this)) return false;
if (this.getName() == null ? other.getValue() != null : !this.getName().equals(other.getName())) return false;
if (this.getValue() == null ? other.getValue() != null : !this.getValue().equals(other.getValue())) return false;
return true;
}
@Override public int hashCode() {
final int PRIME = 59;
int result = 1;
result = (result*PRIME) + (this.getName() == null ? 43 : this.getName().hashCode());
result = (result*PRIME) + (this.getValue() == null ? 43 : this.getValue().hashCode());
return result;
}
}
}
@Setter @Getter
可以作用在类上和属性上,提供默认构造方法。
放在类上,会对所有的非静态(non-static)属性生成Getter/Setter方法,可以定义等级
Lombok 同样为我们提供了一些可以设置当前类似访问权限或者禁止生成某个方法函数,它就是 AccessLevel,其中它对应以下俩种场景:
-
PUBLIC,PROTECTED,PACKAGE,和PRIVATE:设置访问级别;
-
NONE:禁止生成某个对应的 get / set
放在属性上,会对该属性生成Getter/Setter方法。
使用lombok:
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
public class GetterSetterExample {
@Getter @Setter private int age = 10;
@Setter(AccessLevel.PROTECTED) private String name;
@Override public String toString() {
return String.format("%s (age: %d)", name, age);
}
}
不适用lombok:
public class GetterSetterExample {
private int age = 10;
private String name;
@Override public String toString() {
return String.format("%s (age: %d)", name, age);
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
protected void setName(String name) {
this.name = name;
}
}
@Log4j
注解在 类 上;为类提供一个 属性名为 log 的 log4j 日志对象,提供默认构造方法。
使用lombok:
不使用lombok:
@Slf4j:
注解在 类 上;为类提供一个 属性名为 log 的 slf4j日志对象,提供默认构造方法。
@AllArgsConstructor
注解在 类 上;为类提供一个全参的构造方法,加了这个注解后,类中不提供默认构造方法了。
@NoArgsConstructor
注解在 类 上;为类提供一个无参的构造方法。
@RequiredArgsConstructor
这个注解用在 类 上,使用类中所有带有 @NonNull 注解的或者带有 final 修饰的成员变量生成对应的构造方法。
使用lombok:
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.AllArgsConstructor;
import lombok.NonNull;
@RequiredArgsConstructor(staticName = "of")
@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class ConstructorExample<T> {
private int x, y;
@NonNull private T description;
@NoArgsConstructor
public static class NoArgsExample {
@NonNull private String field;
}
}
不使用lombok:
public class ConstructorExample<T> {
private int x, y;
@NonNull private T description;
private ConstructorExample(T description) {
if (description == null) throw new NullPointerException("description");
this.description = description;
}
public static <T> ConstructorExample<T> of(T description) {
return new ConstructorExample<T>(description);
}
@java.beans.ConstructorProperties({"x", "y", "description"})
protected ConstructorExample(int x, int y, T description) {
if (description == null) throw new NullPointerException("description");
this.x = x;
this.y = y;
this.description = description;
}
public static class NoArgsExample {
@NonNull private String field;
public NoArgsExample() {
}
}
}
@EqualsAndHashCode
注解在 类 上, 可以生成 equals、canEqual、hashCode 方法。
使用lombok:
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(exclude={"id", "shape"})
public class EqualsAndHashCodeExample {
private transient int transientVar = 10;
private String name;
private double score;
private Shape shape = new Square(5, 10);
private String[] tags;
private int id;
public String getName() {
return this.name;
}
@EqualsAndHashCode(callSuper=true)
public static class Square extends Shape {
private final int width, height;
public Square(int width, int height) {
this.width = width;
this.height = height;
}
}
}
@NonNull
注解在 属性 上,会自动产生一个关于此参数的非空检查,如果参数为空,则抛出一个空指针异常,也会有一个默认的无参构造方法。
使用lombok:
import lombok.NonNull;
public class NonNullExample extends Something {
private String name;
public NonNullExample(@NonNull Person person) {
super("Hello");
this.name = person.getName();
}
}
不使用lombok:
import lombok.NonNull;
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();
}
}
@Cleanup
这个注解用在 变量 前面,可以保证此变量代表的资源会被自动关闭,默认是调用资源的 close() 方法,如果该资源有其它关闭方法,可使用 @Cleanup(“methodName”) 来指定要调用的方法,也会生成默认的构造方法
使用lombok:
import lombok.Cleanup;
import java.io.*;
public class CleanupExample {
public static void main(String[] args) throws IOException {
@Cleanup InputStream in = new FileInputStream(args[0]);
@Cleanup OutputStream out = new FileOutputStream(args[1]);
byte[] b = new byte[10000];
while (true) {
int r = in.read(b);
if (r == -1) break;
out.write(b, 0, r);
}
}
}
不使用lombok:
import java.io.*;
public class CleanupExample {
public static void main(String[] args) throws IOException {
InputStream in = new FileInputStream(args[0]);
try {
OutputStream out = new FileOutputStream(args[1]);
try {
byte[] b = new byte[10000];
while (true) {
int r = in.read(b);
if (r == -1) break;
out.write(b, 0, r);
}
} finally {
if (out != null) {
out.close();
}
}
} finally {
if (in != null) {
in.close();
}
}
}
}
@ToString
使用exclude属性来控制某几个字段不出现在toString方法的结果中;
使用includeFieldNames,of 属性来控制是否在toString方法的结果中出现成员变量的名称;
使用doNotUseGetters属性来控制在toString方法中是否使用getter方法来访问变量的值;
使用callSuper属性来控制是否要调用父类的toString方法,即在子类的toString方法输出时,是否同时将父类的成员一同输
这个注解用在 类 上,默认情况下,会输出类名、所有属性,属性会按照顺序输出,以逗号分割,还会生成默认的构造方法。
使用lombok:
import lombok.ToString;
@ToString(exclude="id")
public class ToStringExample {
private static final int STATIC_VAR = 10;
private String name;
private Shape shape = new Square(5, 10);
private String[] tags;
private int id;
public String getName() {
return this.getName();
}
@ToString(callSuper=true, includeFieldNames=true)
public static class Square extends Shape {
private final int width, height;
public Square(int width, int height) {
this.width = width;
this.height = height;
}
}
}
不使用lombok:
import java.util.Arrays;
public class ToStringExample {
private static final int STATIC_VAR = 10;
private String name;
private Shape shape = new Square(5, 10);
private String[] tags;
private int id;
public String getName() {
return this.getName();
}
public static class Square extends Shape {
private final int width, height;
public Square(int width, int height) {
this.width = width;
this.height = height;
}
@Override public String toString() {
return "Square(super=" + super.toString() + ", width=" + this.width + ", height=" + this.height + ")";
}
}
@Override public String toString() {
return "ToStringExample(" + this.getName() + ", " + this.shape + ", " + Arrays.deepToString(this.tags) + ")";
}
}
@ToString(exclude="column")
意义:排除column列所对应的元素,即在生成toString方法时不包含column参数;
@ToString(exclude={"column1","column2"})
意义:排除多个column列所对应的元素,其中间用英文状态下的逗号进行分割,即在生成toString方法时不包含多个column参数;
@ToString(of="column")
意义:只生成包含column列所对应的元素的参数的toString方法,即在生成toString方法时只包含column参数;;
@ToString(of={"column1","column2"})
意义:只生成包含多个column列所对应的元素的参数的toString方法,其中间用英文状态下的逗号进行分割,即在生成toString方法时只包含多个column参数;
@Value 可参考@Data
这个注解用在 类 上,会生成含所有参数的构造方法,get 方法,此外还提供了equals、hashCode、toString 方法。
@SneakyThrows
这个注解用在 方法 上,可以将方法中的代码用 try-catch 语句包裹起来,捕获异常并在 catch 中用 Lombok.sneakyThrow(e) 把异常抛出,可以使用 @SneakyThrows(Exception.class) 的形式指定抛出哪种异常,也会生成默认的构造方法。
使用lombok:
import lombok.SneakyThrows;
import java.io.FileInputStream;
import java.io.IOException;
public class SneakyThrowsTest {
@SneakyThrows(IOException.class)
public static void main(String[] args) {
new FileInputStream(args[0]);
}
}
不使用lombok:
import java.io.FileInputStream;
import java.io.IOException;
public class SneakyThrowsTest {
public SneakyThrowsTest() {
}
public static void main(String[] args) {
try {
new FileInputStream(args[0]);
} catch (IOException var2) {
throw var2;
}
}
}
@Synchronized
这个注解用在 类方法 或者 实例方法 上,效果和 synchronized 关键字相同,区别在于锁对象不同,对于类方法和实例方法,synchronized 关键字的锁对象分别是类的 class 对象和 this 对象,而 @Synchronized 的锁对象分别是 私有静态 final 对象 lock 和 私有 final 对象 lock,当然,也可以自己指定锁对象,此外也提供默认的构造方法。
使用lombok:
import lombok.Synchronized;
public class SynchronizedTest {
private final Object readLock = new Object();
@Synchronized
public static void sayHello(){
System.out.println("hello world");
}
@Synchronized
public int sayName(){
return 30;
}
@Synchronized("readLock")
public void read(){
System.out.println("I am reading");
}
}
不使用lombok:
public class SynchronizedTest {
private static final Object $LOCK = new Object[0];
private final Object $lock = new Object[0];
private final Object readLock = new Object();
public SynchronizedTest() {
}
public static void sayHello() {
Object var0 = $LOCK;
synchronized($LOCK) {
System.out.println("hello world");
}
}
public int sayName() {
Object var1 = this.$lock;
synchronized(this.$lock) {
return 30;
}
}
public void read() {
Object var1 = this.readLock;
synchronized(this.readLock) {
System.out.println("I am reading");
}
}
七、@Accessors(chain = true)
该注解配合 @Getter @Setter 可进行链式编程,美化代码
@Accessors(chain = true)
@Getter
@Setter
public class StudentBean {
private String name;
private int age;
public static void main(String[] args) {
StudentBean studentBean = new StudentBean().setName("test").setAge(1);
System.out.println("name:" +studentBean.getName());
}
}
八、Lombok工作原理分析
会发现在Lombok使用的过程中,只需要添加相应的注解,无需再为此写任何代码。自动生成的代码到底是如何产生的呢?
核心之处就是对于注解的解析上。JDK5引入了注解的同时,也提供了两种解析方式。
- 运行时解析
运行时能够解析的注解,必须将@Retention设置为RUNTIME,这样就可以通过反射拿到该注解。java.lang,reflect反射包中提供了一个接口AnnotatedElement,该接口定义了获取注解信息的几个方法,Class、Constructor、Field、Method、Package等都实现了该接口,对反射熟悉的朋友应该都会很熟悉这种解析方式。
- 编译时解析
编译时解析有两种机制,分别简单描述下:
1)Annotation Processing Tool
apt自JDK5产生,JDK7已标记为过期,不推荐使用,JDK8中已彻底删除,自JDK6开始,可以使用Pluggable Annotation Processing API来替换它,apt被替换主要有2点原因:
- api都在com.sun.mirror非标准包下
- 没有集成到javac中,需要额外运行
2)Pluggable Annotation Processing API
JSR 269自JDK6加入,作为apt的替代方案,它解决了apt的两个问题,javac在执行的时候会调用实现了该API的程序,这样我们就可以对编译器做一些增强,这时javac执行的过程如下:
Lombok本质上就是一个实现了“JSR 269 API”的程序。在使用javac的过程中,它产生作用的具体流程如下:
- javac对源代码进行分析,生成了一棵抽象语法树(AST)
- 运行过程中调用实现了“JSR 269 API”的Lombok程序
- 此时Lombok就对第一步骤得到的AST进行处理,找到@Data注解所在类对应的语法树(AST),然后修改该语法树(AST),增加getter和setter方法定义的相应树节点
- javac使用修改后的抽象语法树(AST)生成字节码文件,即给class增加新的节点(代码块)
九、Lombok的优缺点
优点:
- 能通过注解的形式自动生成构造器、getter/setter、equals、hashcode、toString等方法,提高了一定的开发效率
- 让代码变得简洁,不用过多的去关注相应的方法
- 属性做修改时,也简化了维护为这些属性所生成的getter/setter方法等
缺点:
- 不支持多种参数构造器的重载
- 虽然省去了手动创建getter/setter方法的麻烦,但大大降低了源代码的可读性和完整性,降低了阅读源代码的舒适度
- 通过idea工具生成java doc 文档时,无法生成实体类的get set方法