前言
Lombok实现原理简析。
JCP与JSR
JCP(Java Community Process)是管理 Java 生态(包括 J2SE、J2EE 等等)发展及指导和推动Java平台发展的合作组织,而JSR(Java Specification Request)则是由JCP提出的一种规范请求。
JCP是由Sun Microsystems(现在是Oracle Corporation)创建的一个组织,负责制定和管理Java技术的发展。它包括了一系列专家组和工作组,这些组织和个人共同合作制定和更新Java规范。
JSR 是 Java 规范请求。这是由一个或多个成员提交给 PMO 的文件,以提议开发新规范或对现有规范进行重大修订。 JCP 计划目前正在开发许多 Java 技术规范,包括 Java™ Micro Edition (Java ME™)、Java™ Platform Enterprise Edition (Java EE™) 和 Java™ Standard Edition (Java SE™) 的下一版本。 JSR 还指由这些提案产生的规范开发工作。
JSR经历多个阶段,包括草案、提案、公开评论、维护和最终发布等。
PMO 即 Program Management Office,项目管理办公室是 Oracle 内部负责监督 Java 社区流程并管理项目日常运行的小组。该规范的实际开发是在专家组内进行的。
JCP:https://jcp.org/en/home/index
JSR-000269
JSR-000269(JSR-269)的目标是定义并实现Java编译时注解处理器的标准API,或称为可插拔注解处理(Pluggable Annotation Processing),该JSR提供了一种机制,使开发人员能够在Java源代码编译过程中访问和处理注解,从而实现更高级的语义效果和自动化任务。
要完全的了解JSR-269的前因后果及技术细节,不应该在本文中寻找答案,本文仅铺垫一些形象化描述Lombok实现原理所必需的前置知识。总之,通过JSR-269的实现,开发人员可以利用编译时注解处理器的能力,以一种标准化和可扩展的方式对源代码进行自动化处理。这种处理包括生成额外的代码、进行静态分析、验证约束条件等。
JSR-000269的原始提出第一节如下:
J2SE 1.5引入了一种新的Java语言机制“注解”,允许使用注解类型对类、字段和方法进行注解。这些注解通常由编译时工具或运行时库来处理,以实现新的语义效果。为了支持编译时的注解处理,这个JSR将定义API,允许使用标准可插拔的API创建注解处理器。这将简化创建注解处理器的任务,并且还可以自动发现适用于给定源文件的合适的注解处理器。
该规范将包括至少两个部分:一个部分是对Java编程语言进行建模的API部分,另一个部分用于声明注解处理器并控制它们的运行方式。由于注解是放置在程序元素上的,注解处理框架需要反映程序结构。注解处理器将能够指定它们处理的注解,并且多个处理器将能够合作运行。
处理器和程序结构API可以在编译时访问;即此功能补充了核心反射支持读取注解的能力。
Lombok代码注入原理
Lombok使用了JSR-269的API,即javax.annotation.processing
下的一组接口,在编译期时把 Lombok 的注解代码,转换为常规的 Java ⽅法⽽实现注⼊。
使用 javac
进行编译时,Lombok生成目标方法的流程如下:
- 首先
javac
对源代码进行分析生成一棵抽象语法树(AST) - 接着在运行过程中调用实现了 JSR-269 API 的 lombok 程序
- 接着编译器会调用 lombok 程序对上面得到的抽象语法树 AST 进行处理,找到其注解所在类对应的语法树(AST),然后修改该语法树,增加注解对应的方法或代码片段到定义的相应树节点
javac
使用修改后的抽象语法树生成最终的 class 文件
利用269API实现Setter注解
1、⾃定义注解1
2
3
4
5
6package lombok.setter;
public MySetter {
}
2、自定义注解处理器
注解处理器是代码生成的核心,对语法树的操作需要使用自己JDK的tools.jar
,涉及到的核心库主要为com.sun.tools.javac
。
1 | // author: SilenceZheng66 |
3、编译注解处理器
之后所有操作都在MySetter的目录下进行。
1 | javac -cp /path/to/tools.jar MySetter* -d . |
4、编写和编译目标类
目标类:1
2
3
4
5
6
public class TestMySetter {
public Integer intt;
public String strr;
public Byte bytee;
}
用自定义注解处理器编译目标类:javac -processor lombok.setter.MySetterProcessor TestMySetter.java
编译结果如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20public class TestMySetter {
public Integer intt;
public String strr;
public Byte bytee;
public void setBytee(Byte var1) {
this.bytee = var1;
}
public void setStrr(String var1) {
this.strr = var1;
}
public void setIntt(Integer var1) {
this.intt = var1;
}
public TestMySetter() {
}
}
Lombok插件
前面形象化的讲述了Lombok的代码生成原理,但这些都发生在编译期,代码编辑器如何获取到这些信息并提前应用到代码提示上呢?Lombok给主流集成开发环境编写了Lombok插件,在使用Lombok的过程中不需要编译即可实现代码提示和代码补全,相关源码都公开在GitHub上(见[9]),有兴趣的读者可以自行阅读研究。
参考
[1] https://developer.aliyun.com/article/1081626
[2] https://blog.csdn.net/shouchenchuan5253/article/details/111658356
[3] https://blog.csdn.net/weixin_43983762/article/details/105867398
[4] https://juejin.cn/post/7103011031672176677
[5] https://jcp.org/en/jsr/detail?id=269
[6] https://jcp.org/aboutJava/communityprocess/mrel/jsr269/index6.html
[7] https://projectlombok.org/
[8] https://docs.oracle.com/javase/7/docs/technotes/guides/apt/GettingStarted.html
[9] https://github.com/projectlombok/lombok
后记
首发于 silencezheng.top,转载请注明出处。