1.前言
Java源码中使用Lombok的@Data注解:
编译后的class文件,自动添加了一些方法
在编译期为代码进行了增强,如何做了增强,为什么会这样?
2.编译器在编译期间做的操作
我们都知道@Data注解,@Retention(RetentionPolicy.SOURCE) ,这个的意思是,注解保留的时间是在源码期间,编译后该注解就会删除,在删除之前,编译器会为其添加一些方法等等操作,即在编译期为代码进行了增强。
3.原理
3.1JSR-269提案
在JDK6提出并通过了JSR-269提案,提案通过了一组被称为“插入式注解处理器”的标准API,可以提前至编译期对代码中的特定注解进行处理, 从而影响到编译器的工作过程。
3.2Java语言实现
对于底层的一些实现,普遍会认为实现是像虚拟机一样使用C++实现,对于Java程序员来说并不是特别友好。但是Javac编译器是使用Java实现的,更容易上手。
3.3Javac的编译过程
Javac的编译过程大致分为几步
- 准备过程:初始化插入式注解处理器
- 解析与填充符号表过程 词法、语法分析构建抽象语法树(AST)
- 插入式注解处理器的注解处理过程
- 分析与字节码生成过程
- 语法树变动后会再次解析与填充符号表,语法树没有变动时编译器就不会再对源码字符流操作,而是基于抽象语法树
综上所述想实现Lombok的效果只需要遵守JSR-269在编译期对AST进行操作即可实现 当然不止有Lombok通过这种方式实现,例如FindBug、MapStruct等也通过这种方式实现
3.4 Lombok在编译过程中如何做的
Lombok本质上就是一个实现了“JSR 269 API”的程序。在使用javac的过程中,它产生作用的具体流程如下:
- javac对源代码进行分析,生成了一棵抽象语法树(AST)
- 运行过程中调用实现了“JSR 269 API”的Lombok程序
- 此时Lombok就对第一步骤得到的AST进行处理,找到@Data注解所在类对应的语法树(AST),然后修改该语法树(AST),增加getter和setter方法定义的相应树节点
- javac使用修改后的抽象语法树(AST)生成字节码文件,即给class增加新的节点(代码块)
3.5优缺点
优点:
- 能通过注解的形式自动生成构造器、getter/setter、equals、hashcode、toString等方法,提高了一定的开发效率
- 让代码变得简洁,不用过多的去关注相应的方法
- 属性做修改时,也简化了维护为这些属性所生成的getter/setter方法等
缺点:
- 不支持多种参数构造器的重载
- 虽然省去了手动创建getter/setter方法的麻烦,但大大降低了源代码的可读性和完整性,降低了阅读源代码的舒适度
总结:
- 像 lombok 这种插件,已经不仅仅是插件了,它在编译器编译时通过操作AST(抽象语法树)改变字节码生成,变相的说它就是在改变java语法,它改变了你编写源码的方式,它不像 spring 的依赖注入一样是运行时的特性,而是编译时的特性。如果一个项目有非常多这样的插件,会极大的降低阅读源代码的舒适度。
- lombok 只是省去了一些人工生成代码的麻烦,但是这些getter/setter等等的方法,用IDE的快捷键也可很方便的生成。况且,有时通过给getter/setter加一点点业务代码(但通常不建议这么加),能极大的简化某些业务场景的代码。