一、Lombok 是什么
-
Project Lombok is a java library that automatically plugs into your editor and build tools, spicing up your java.
Never write another getter or equals method again, with one annotation your class has a fully featured builder, Automate your logging variables, and much more. -
中文翻译
Project Lombok
是一个Java
库,它可以自动插入你的编辑器和构建工具中,为你的Java
添彩。
再也不用写另一个getter
或equals
方法了,只需一个注释,你的类就有了一个功能齐全的构建器,自动记录变量,还有更多。 -
白话解释
Lombok
是一种Java
实用工具,可以用来帮助开发人员消除Java
中冗长的固定代码(get、set、toString、hashCode、equals… …),尤其是对Java
对象(POJO
),它通过注解来实现这一目的 -
Lombok
原理-
JSR 269(点我了解详情)
插件化注解处理API
(Pluggable Annotation Processing API
) -
JSR 269
最大的特点是注解可以在编译期间处理逻辑和修改代码,即:@Retention(RetentionPloicy.SOURCE)
修饰的注解 -
Lombol
编译逻辑处理过程
-
-
JSR 269
原理-
需要使用
JSR 269
规范的自定义插件都需要采用JAVA
的SPI
机制来实现服务的发现 -
自定义注解处理的业务逻辑类都需要继承
AbstractProcessor
类,AbstractProcessor
实现了Processor
接口 -
实现自定义
JSR 269
规范的编译期间注解方式@SupportedAnnotationTypes(value = {"com.xxx.xxx.注解定义接口"}) @SupportedSourceVersion(value = SourceVersion.RELEASE_8) public class MyProcesssor extends AbstractProcessor { @Override public boolean process(Set<? extends TypeElement> annotations, .....) { // 具体注解业务逻辑处理位置 ... ... } }
project - resource - META-INFO.services # 这是一个文件(Spring boot starter 中都 spring.factory 类似) - javax.annotation.processing.Processor # 这是文件中的内容(实际内容顶格且没有 - ) - com.xxx.xxx.MyProcessor
-
更多具体细节可以参考
Lombok
的源码实现
-
二、Lombok 为什么
-
每次定义
JavaBean
的时候,都需要手动生成get()
set()
等固定的模版方法,没有技术含量的同时降低了工作效率 -
每次修改
JavaBean
的时候,都需要手动修改get()
set()
等固定的模版方法,没有技术含量的同时降低了工作效率 -
属性判空操作
@NonNull
可以方便快速的进行业务字段校验(当然Validation
框架也是一个很好的选择)
三、Lombok 怎么用
-
Lombok
安装-
在
IDEA
中安装Lombok
插件(新版本的IntelliJ IDEA
已经集成了Lombok
) 插件,无需再安装(再一次说明了Lombok
的普及性)# 安装步骤 IDEA -> File -> Settings -> Plugins -> MarketPlace -> Lombok 安装后重启 IDEA
-
项目中引入
Lombok
的依赖<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <!-- 可以采用最新版本 --> <version>1.18.22</version> <!-- provided 编译阶--> <scope>provided</scope> </dependency>
-
IDEA
开启自动注解处理功能# 设置步骤 IDEA -> FIle -> Build,Execution,Deployment -> Annotation Processors -> Enable annotation processing(勾选)(JRS 269 的功能都需要勾选)
-
-
常用注解之 ——
@Getter
和@Setter
-
作用范围:
类 和 字段 -
功能说明:
添加到字段上 -> 该字段自动生成getter
和setter
方法和方法的访问级别
添加到类上面 -> 该类成员变量自动生成getter
和setter
方法和方法访问级别 -
使用效果:
P.S
如果成员变量只被
final
修饰,那么会自动生成该变量的getter
方法,不会生成setter
方法
-
-
常用注解之 ——
ToString
-
作用范围:
类 -
功能说明:
重写toString()
方法来覆盖Object
对象中的toString()
,默认重写的方法是该类中所有的成员变量K - V
组合字符串(其中static
修饰的不会被输出) -
使用效果:
@ToString public class User implements Serializable { private static final long serialVersionUID = 4229562213733224501L; private static String flagStatic = ""; private final String flagFinal = "123"; private long id; private String name; private float age; private boolean gender; @Getter(value = AccessLevel.NONE) @Setter(value = AccessLevel.NONE) private String password; private String telephone; private String email; }
// 反编译后的 toString(),其中 static 修饰的不会被输出 public String toString() { return "User(flagFinal=" + this.getFlagFinal() + ", id=" + this.getId() + ", name=" + this.getName() + ", age=" + this.getAge() + ", gender=" + this.isGender() + ", password=" + this.password + ", telephone=" + this.getTelephone() + ", email=" + this.getEmail() + ")"; }
P.S
// exclude 和 of 是互斥的,不能同时使用 @ToString(exclude = {"排除输出字段1,排除输出字段2,..."}, of = {"仅输出字段1,仅输出字段2,..."})
@ToString(of = {"name", "gender"}) public class User implements Serializable { private static final long serialVersionUID = 4229562213733224501L; private static String flagStatic = ""; private final String flagFinal = "123"; private long id; private String name; private float age; private boolean gender; @Getter(value = AccessLevel.NONE) @Setter(value = AccessLevel.NONE) private String password; private String telephone; private String email; }
// 反编译后的 toString(),其中之后 of 包含的字段会被输出 public String toString() { return "User(name=" + this.name + ", gender=" + this.gender + ")"; }
-
-
常用注解之 ——
@EqualsAndHashCode
-
作用范围:
类 -
功能说明:
- 判断类对象是否相等的方法,可以采用默认的模版判断也可以自定义业务规则来判断,同样可以为
@EqualsAndHashCode
设置exclude
和of
属性来包含或者排除某些属性(默认是将类中所有的成员变量都判断一遍,看看是否相等,全部相等才任务两个类相等) Equals
和HashCode
需要保持逻辑上的一致
- 判断类对象是否相等的方法,可以采用默认的模版判断也可以自定义业务规则来判断,同样可以为
-
使用效果:
-
-
常用注解之 ——
@NonNull
-
作用范围:
方法/构造器的参数和成员变量 -
功能说明:
判断该参数不能为null
,如果为null
则抛出NullPointerException
-
使用效果
-
-
常用注解之 ——
@NoArgsConstructor
@RequiredArgsConstructor
@AllArgsConstructor
-
作用范围:
类 -
功能说明:
构造注解 注解含义 @NoArgsConstructor
生成类的无参构造函数 @RequiredArgsConstructor
生成类的被 @NonNull
修饰和final
修饰且没有赋初始值的有参构造函数@AllArgsConstructor
生成类的全参构造函数 -
使用效果:
P.S
以上构造函数注解也可以限定构造函数的修饰符,如:@AllArgsConstructor(access = AccessLevel.PRIVATE)
-
-
常用注解之 ——
@Data
-
作用范围:
类 -
功能说明:
是注解@Getter
@Setter
@RequiredArgsConstructor
@ToString
@EqualsAndHashCode
的组合写法 -
使用效果:
-
-
常用注解之 ——
@Builder
-
作用范围:
类 -
功能说明:
流式编程方式
-
使用效果:
-
-
常用注解之 ——
@Accessors
-
作用范围:
类 -
功能说明:
@Accessors
注解是不作任何事情的,它需要@Data
或者@Getter
和@Setter
注解的协调使用- 链式编程方式
注解属性 程序效果 chain = true 开启链式编程(默认关闭) fluent = true 开启更加流利的链式编程(默认关闭) fluent = true
Lombok
会将setter
getter
方法的前缀set
get
去掉,直接通过属性名称来点出属性getter
setter
-
使用效果:
-
-
常用注解之 ——
@Log
@Slf4j
-
作用范围:
类 -
功能说明:
其实就是替代了创建日志对象的固定写法@Slf4j public class User implements Serializable { // private static final Logger log = LoggerFactory.getLogger(User.class); ... ... }
-
使用效果:
-
-
常用注解之 ——
@Cleanup
-
作用范围:
所有需要关闭流的变量上 -
功能说明:
简化了输入输出流编程中的异常处理和流的关闭问题 -
使用效果:
-
传统写法
@Test void generateIOTest() { String sourceFile = "/Users/rambo/Desktop/4_4_1636623756947.mp3"; String targetFile = "/Users/rambo/Desktop/11111111111111111.mp3"; FileInputStream fis = null; FileOutputStream fos = null; try { // 1、定义文件输入和输出流 fis = new FileInputStream(sourceFile); fos = new FileOutputStream(targetFile); // 2、定义临时的文件缓冲区 byte[] bytes = new byte[10240]; // 3、从文件输入流拷贝文件到输出流(没有读完继续读,直到读完为止) int contentLength = 0; while ((contentLength = fis.read(bytes)) != -1) { // 4、将读到临时文件缓冲区的内容写入到输出流 fos.write(bytes, 0, contentLength); } } catch (IOException e) { e.printStackTrace(); } finally { // 5、关闭流 if (fis != null) { try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } if (fos != null) { try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } } }
-
JDK 8
的优雅写法@Test void newerIOTest() { String sourceFile = "/Users/rambo/Desktop/4_4_1636623756947.mp3"; String targetFile = "/Users/rambo/Desktop/22222222222222222.mp3"; try( // 1、定义文件输入和输出流 InputStream is = new FileInputStream(sourceFile); OutputStream os = new FileOutputStream(targetFile)) { // 2、定义临时的文件缓冲区 byte[] bytes = new byte[10240]; // 3、从文件输入流拷贝文件到输出流(没有读完继续读,直到读完为止) int contentLength = 0; while ((contentLength = is.read(bytes)) != -1) { // 4、将读到临时文件缓冲区的内容写入到输出流 os.write(bytes, 0, contentLength); } } catch (IOException e) { e.printStackTrace(); } }
-
Lombok
写法@Test void lombokIOTest() throws IOException { String sourceFile = "/Users/rambo/Desktop/4_4_1636623756947.mp3"; String targetFile = "/Users/rambo/Desktop/11111111111111111.mp3"; // 1、定义文件输入和输出流 @Cleanup InputStream fis = new FileInputStream(sourceFile); @Cleanup OutputStream fos = new FileOutputStream(targetFile); // 2、定义临时的文件缓冲区 byte[] bytes = new byte[10240]; // 3、从文件输入流拷贝文件到输出流(没有读完继续读,直到读完为止) int contentLength = 0; while ((contentLength = fis.read(bytes)) != -1) { // 4、将读到临时文件缓冲区的内容写入到输出流 fos.write(bytes, 0, contentLength); } }
-
-
P.S
Lombok
采用 JSR 269
特性通过注解来修改 AST
语法树生成的固定模版方法时,如果对某个指定模版进行来手动编码,那么手动编码的优先级高于 Lombok
生成的模版编码