先说结论吧,能不用尽量不用,如果已经用了,那就用好。 会用不等于用的对,所以你有必要了解一下到底什么是Lombok,而不单单只是感觉我写个注解,帮我省去了好多代码,好棒,笑cry。 在示例代码部分,我直接从Lombok的官网复制过来的,而且并没有对应的DeLombok代码,如果你想知道,最好的方法就是,在你的电脑上编译一遍我给的代码,然后反编译查看源码,这样能加深你对Lombok的理解。说白了,就是我懒。 ### 什么是Lombok? 看官方的一段介绍吧: > Project Lombok makes java a spicier language by adding 'handlers' that know how to build and compile simple, boilerplate-free, not-quite-java code. 意思就是通过增加一些处理器,使得Java可以构建和编译更简洁、无样本文件、不完全是Java的代码。 而这些处理程序是通过注解的形式使用的,只要你在相应的Bean上添加了注解,就会有相应的处理程序在编译代码时,对代码做对应修改,这样生成的字节码文件中就会有我们需要的内容。例如我们常见的POJO,私有属性一般通过getter/setter方法进行访问。如果修改/删除/添加属性,还需要同步更新对应的getter/setter方法,如果忘记了,就很容易出问题。在类上使用@Getter和@Setter注解后,b编译生成的字节码文件会为所有属性添加对应的getter/setter方法。如此一来,极大的简化了Bean的维护难度。 ### 如何使用Lombok? 首先第一步需要引入对应的依赖。 ```xml org.projectlombok lombok 1.16.20 provided ``` 一般我们使用IDEA作为集成开发环境还需要安装对应的插件,这样在调用使用Lombok注解生成的getter/setter等方法时才不会报错。现在较新版本的IDEA一般自带这个插件,如果你发现报错的话,可以自己检查一下是不是有这个插件在,如果不在或者时禁用的状态,安装或者重新启用一下试试。 ### Lombok有哪些注解? Lombok的引入非常的简单,但是Lombok的注解大部分人只知道@Data,如果你想更好的使用Lombok,最好深入了解一个Lombok有哪些注解,以及这些注解的用途,所谓用途,就是你需要知道这个注解替我们做了哪些事情,如果不使用这些注解,我们的代码需要怎么写。 #### @Data Lombok最常用的注解,如果使用在类上,会为这个类的所有属性生成对应的getter/setter方法以及覆写eauqls、hashCode、toString方法,如果为final类型,则不会生成对应的setter方法。 示例如下: ```java @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 { private final String name; private final T value; } } ``` #### @Getter/@Setter 由于@Data过于暴力,一般我只需要getter/setter方法就可以了,我是不会直接使用@Data的,需要什么就使用对应的注解,这样可以做到精细化的访问控制。 @Getter/@Setter可以使用在类上,可以为所有属性生成对应的getter/setter方法,final类型的属性不会生成setter方法。同时,@Getter/@Setter也可以使用在具体的某个属性上,为某个或几个属性生成getter/setter方法。 示例如下: ```java 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); } } ``` #### @NonNull 这个注解可以使用在属性或者是构造器上,Lombok会生成一个非空的声明,可以用于校验参数,防止空指针异常。 ```java public class NonNullExample extends Something { private String name; public NonNullExample(@NonNull Person person) { super("Hello"); this.name = person.getName(); } } ``` #### @Cleanup 这个注解使用的比较少,可以说我基本上没用过。这个注解的作用是能够帮助我们自动调用close方法,在一些资源需要释放的场景下,会很方便。前提是被注解的资源需要实现CloseAble接口。 示例如下: ```java 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); } } } ``` #### @EqualsAndHashCode 这个注解会帮助我们生成equals和hashCode方法,这两个方法常和集合框架配合在一起使用,使用不当会极大影响性能,例如Map中本来线性级的查找,将会变成指数极的查找。 默认情况下,@EqualsAndHashCode注解会使用所有非静态(非static)的和非瞬态(非transient)的属性,生成equals和hashCode方法。我们也可以使用注解的exclude参数,排除某一些属性。 示例如下: ```java @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; } } } ``` #### @ToString 在类上使用这个注解,会帮助我们覆写toString方法,默认会输出类名,所有属性值,并且按照逗号分隔。通常我们使用注解的参数includeFieldName属性,将之设置为true,这样输出的string中会包含属性名称,会更加易于阅读。 示例如下: ```java @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; } } } ``` #### @NoArgsConstructor, @RequiredArgsConstructor, @AllArgsConstructor 这三个注解放在一起,都是生成构造器的,分别是无参构造器,部分参数构造器,全参构造器。很遗憾,Lombok并不能实现不同参数构造器的重载,只能生成三种,可能原因在于@RequiredArgsConstructor没有使用@Repeatable元注解,所以只能使用一次。@Repeatable是Java8新增的一个元注解,我想Lombok应该是为了兼容性考虑吧。 示例如下: ```java @RequiredArgsConstructor(staticName = "of") @AllArgsConstructor(access = AccessLevel.PROTECTED) public class ConstructorExample { private int x, y; @NonNull private T description; @NoArgsConstructor public static class NoArgsExample { @NonNull private String field; } } ``` ### Lombok原理剖析 开头提到了,Lombok会增加一个些处理器,在编译的时候,在生成的字节码文件中添加对应的方法。如果你了解注解,应该知道,注解本身并不改变程序的任何结构,对代码的处理是注解对应的处理程序所做的工作。 Java的注解有两种解析模式,一种是运行期进行解析和处理,另一种是编译器进行解析和处理。显而易见的,Lombok是在编译期解析的。编译期解析有两种机制,一个是Annotation Processing Tool,另一个是Pluggable Annotation Processing API,前者子JDK1.6开始就被标记为废弃,可以用后者代替。有兴趣的读者可以自行深入了解一下。 LomBok本质上就是一个实现了`JSR 269 API`的程序,在使用javac编译的时候,它做了下面这些事情: 1. javac对源代码进行分析,生成了一棵抽象语法树(AST); 2. 运行过程中调用实现了“JSR 269 API”的Lombok程序; 3. 此时Lombok就对第一步骤得到的AST进行处理,找到@Data注解所在类对应的语法树(AST),然后修改该语法树(AST),增加getter和setter方法定义的相应树节点; 4. javac使用修改后的抽象语法树(AST)生成字节码文件,即给class增加新的节点(代码块)。 ### 该不该使用Lombok? 我觉得这是个见仁见智的问题,罗卜青菜各有所爱,支持的人说,使用Lombok可以使得代码看起来更简洁,减少代码量;反对的人说,Lombok改变了Java的语法(所以要依赖于插件),降低了源码的可读性。要我说,一个工具而已,如果项目中有人在用了,你也跟着用就是了。与其花时间在这些没有意义的争论上,不如多花点时间,重构一下那坨看了一遍就不想看第二遍的狗屎代码。说的我都笑了。 Loading... 先说结论吧,能不用尽量不用,如果已经用了,那就用好。 会用不等于用的对,所以你有必要了解一下到底什么是Lombok,而不单单只是感觉我写个注解,帮我省去了好多代码,好棒,笑cry。 在示例代码部分,我直接从Lombok的官网复制过来的,而且并没有对应的DeLombok代码,如果你想知道,最好的方法就是,在你的电脑上编译一遍我给的代码,然后反编译查看源码,这样能加深你对Lombok的理解。说白了,就是我懒。 ### 什么是Lombok? 看官方的一段介绍吧: > Project Lombok makes java a spicier language by adding 'handlers' that know how to build and compile simple, boilerplate-free, not-quite-java code. 意思就是通过增加一些处理器,使得Java可以构建和编译更简洁、无样本文件、不完全是Java的代码。 而这些处理程序是通过注解的形式使用的,只要你在相应的Bean上添加了注解,就会有相应的处理程序在编译代码时,对代码做对应修改,这样生成的字节码文件中就会有我们需要的内容。例如我们常见的POJO,私有属性一般通过getter/setter方法进行访问。如果修改/删除/添加属性,还需要同步更新对应的getter/setter方法,如果忘记了,就很容易出问题。在类上使用@Getter和@Setter注解后,b编译生成的字节码文件会为所有属性添加对应的getter/setter方法。如此一来,极大的简化了Bean的维护难度。 ### 如何使用Lombok? 首先第一步需要引入对应的依赖。 ```xml <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.20</version> <scope>provided</scope> </dependency> ``` 一般我们使用IDEA作为集成开发环境还需要安装对应的插件,这样在调用使用Lombok注解生成的getter/setter等方法时才不会报错。现在较新版本的IDEA一般自带这个插件,如果你发现报错的话,可以自己检查一下是不是有这个插件在,如果不在或者时禁用的状态,安装或者重新启用一下试试。 ### Lombok有哪些注解? Lombok的引入非常的简单,但是Lombok的注解大部分人只知道@Data,如果你想更好的使用Lombok,最好深入了解一个Lombok有哪些注解,以及这些注解的用途,所谓用途,就是你需要知道这个注解替我们做了哪些事情,如果不使用这些注解,我们的代码需要怎么写。 #### @Data Lombok最常用的注解,如果使用在类上,会为这个类的所有属性生成对应的getter/setter方法以及覆写eauqls、hashCode、toString方法,如果为final类型,则不会生成对应的setter方法。 示例如下: ```java @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; } } ``` #### @Getter/@Setter 由于@Data过于暴力,一般我只需要getter/setter方法就可以了,我是不会直接使用@Data的,需要什么就使用对应的注解,这样可以做到精细化的访问控制。 @Getter/@Setter可以使用在类上,可以为所有属性生成对应的getter/setter方法,final类型的属性不会生成setter方法。同时,@Getter/@Setter也可以使用在具体的某个属性上,为某个或几个属性生成getter/setter方法。 示例如下: ```java 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); } } ``` #### @NonNull 这个注解可以使用在属性或者是构造器上,Lombok会生成一个非空的声明,可以用于校验参数,防止空指针异常。 ```java public class NonNullExample extends Something { private String name; public NonNullExample(@NonNull Person person) { super("Hello"); this.name = person.getName(); } } ``` #### @Cleanup 这个注解使用的比较少,可以说我基本上没用过。这个注解的作用是能够帮助我们自动调用close方法,在一些资源需要释放的场景下,会很方便。前提是被注解的资源需要实现CloseAble接口。 示例如下: ```java 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); } } } ``` #### @EqualsAndHashCode 这个注解会帮助我们生成equals和hashCode方法,这两个方法常和集合框架配合在一起使用,使用不当会极大影响性能,例如Map中本来线性级的查找,将会变成指数极的查找。 默认情况下,@EqualsAndHashCode注解会使用所有非静态(非static)的和非瞬态(非transient)的属性,生成equals和hashCode方法。我们也可以使用注解的exclude参数,排除某一些属性。 示例如下: ```java @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; } } } ``` #### @ToString 在类上使用这个注解,会帮助我们覆写toString方法,默认会输出类名,所有属性值,并且按照逗号分隔。通常我们使用注解的参数includeFieldName属性,将之设置为true,这样输出的string中会包含属性名称,会更加易于阅读。 示例如下: ```java @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; } } } ``` #### @NoArgsConstructor, @RequiredArgsConstructor, @AllArgsConstructor 这三个注解放在一起,都是生成构造器的,分别是无参构造器,部分参数构造器,全参构造器。很遗憾,Lombok并不能实现不同参数构造器的重载,只能生成三种,可能原因在于@RequiredArgsConstructor没有使用@Repeatable元注解,所以只能使用一次。@Repeatable是Java8新增的一个元注解,我想Lombok应该是为了兼容性考虑吧。 示例如下: ```java @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原理剖析 开头提到了,Lombok会增加一个些处理器,在编译的时候,在生成的字节码文件中添加对应的方法。如果你了解注解,应该知道,注解本身并不改变程序的任何结构,对代码的处理是注解对应的处理程序所做的工作。 Java的注解有两种解析模式,一种是运行期进行解析和处理,另一种是编译器进行解析和处理。显而易见的,Lombok是在编译期解析的。编译期解析有两种机制,一个是Annotation Processing Tool,另一个是Pluggable Annotation Processing API,前者子JDK1.6开始就被标记为废弃,可以用后者代替。有兴趣的读者可以自行深入了解一下。 LomBok本质上就是一个实现了`JSR 269 API`的程序,在使用javac编译的时候,它做了下面这些事情: 1. javac对源代码进行分析,生成了一棵抽象语法树(AST); 2. 运行过程中调用实现了“JSR 269 API”的Lombok程序; 3. 此时Lombok就对第一步骤得到的AST进行处理,找到@Data注解所在类对应的语法树(AST),然后修改该语法树(AST),增加getter和setter方法定义的相应树节点; 4. javac使用修改后的抽象语法树(AST)生成字节码文件,即给class增加新的节点(代码块)。 ### 该不该使用Lombok? 我觉得这是个见仁见智的问题,罗卜青菜各有所爱,支持的人说,使用Lombok可以使得代码看起来更简洁,减少代码量;反对的人说,Lombok改变了Java的语法(所以要依赖于插件),降低了源码的可读性。要我说,一个工具而已,如果项目中有人在用了,你也跟着用就是了。与其花时间在这些没有意义的争论上,不如多花点时间,重构一下那坨看了一遍就不想看第二遍的狗屎代码。说的我都笑了。 最后修改:2021 年 08 月 03 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 如果觉得我的文章对你有用,请随意赞赏