前言
写Java代码的时候,我讨厌写setter/getter方法。既然不用再用Lombok插件写那些方法了,感觉回不去了。你有没有想过Lombok是怎么给你添加setter/getter方法的?有同学说Java引入Lombok后会污染依赖包,那么能不能写个工具替代Lombok?

学习点
Java编译程序,了解Lombok原理,了解插件注释处理器
分析
前言中提到的问题其实都是同一个问题,就是如何获取和修改Java源代码。
要回答这个问题,我们需要回答这些问题:
Java编译器如何解析Java源代码?编译器编译源代码的步骤有哪些?当我们在编译器中工作时,如何添加内容或进行代码分析?
希望你看完这篇文章能自己写一个简单的Lombok工具。
回答
如何解析源代码
实际上,在我们的代码和它的编译之间有一个叫做AST的数据结构。具体形式,可以看下图。右边是AST的数据结构。
编译代码的步骤有哪些?
整个编译过程大致如下:
图片来自openjdk
1.初始化插入注释处理器。
2.解析和填充符号表的过程
A.词汇分析和语法分析。将源代码的字符流转换成标记集,并构造抽象语法树。
B.填写符号表。生成符号地址和符号信息。
3.插件注释处理器的注释过程:插件注释处理器的执行阶段。后面我会给大家带来两个这方面的实际例子。
4.分析和字节码生成过程
A.标签检查。语法的静态信息检查。
B.数据流和控制流分析。检查程序的动态运行过程。
C.语法糖。把简化代码编写的语法糖还原成原来的样子。
D.字节码生成。将前面步骤中生成的信息转换成字节码。
了解了以上理论之后,我们来进行实战。带大家复习AST。添加您自己的代码。
真正的战斗
如何自己实现自动添加Setter/Getter的工具
首先,我们创建自己的注释。
@Retention //注释仅保留在用于修改类public @ interface my setter getter { }的源代码@Target //中
创建一个需要生成setter/getter方法的实体类。
@MySetterGetter //标记我们的标注公共类Test { Private String WZJ}
让我们看看如何生成我们想要的字符串。
整体代码如下:
@ supportedannotationtypes @ supportedsourceversionpublic类mysettergetter processor扩展抽象处理器{//主要是输出信息private Messager messager私有JavacTrees javacTrees私人造树师造树师;私人姓名;@Override公共同步void init { super.initthis . messager = processing env . getmessager;this . javactrees = javactrees . instance;Context context = processingEnv)。getContextthis . tree maker = tree maker . instance;this . names = names . instance;} @ override public boolean process {//获取所有带注释的类集< extends element > elements statedwith = roundenv . getelements statedwith;elementsAnnotatedWith.forEach//遍历类并修改类树,accept { @ override public void visit class def { list jcvariable declist = list . nil;//查找{ if . equals){ JC tree . jcvariabledecl jcvariabledecl = JC tree的抽象树中的所有变量;jcvariabledeclist = jcvariabledeclist . append;} }//执行为{message.printmessage+"已处理)的变量生成方法的操作);jcclassdecl . defs = jcclassdecl . defs . prepend);jcclassdecl . defs = jcclassdecl . defs . prepend);}//生成返回对象jctree.jcexpression方法type = tree maker . type);返回treeMaker。MethodDef,getNewSetterMethodName),methodType,List.nil,parameters,List.nil,block,null);}private jctree . jcmethoddecl makegetMethodDecl { list buffer statements = new list buffer;//生成表达式jctree . JC return are turn = tree maker . return));语句. append;JCTree。JCBlock block = treeMaker。块);//无参数//生成返回对象jctree.jcexpression返回类型= tree maker . type . type);返回treeMaker。MethodDef,getNewGetterMethodName),returnType,List.nil,List.nil,List.nil,block,null);}private name getnewsettermethodname { strings = name . tostring;return names . from string . toupper case+s . substring));}private name getnewgetgetmethodname { strings = name . tostring;return names . from string . toupper case+s . substring));}private jctree . jcexpressionstatement make赋值{ return treemaker . exec);}}
有几个代码。下面我们一个一个的拆解说明:
这里是整个代码结构的脑图,下面的讲解将以此为序。
A.笔记
@SupportedAnnotationTypes表示我们需要监控的注释,比如我们前面定义的@ MySetterGetter。
@SupportedSourceVersion表示我们要处理哪个版本的Java源代码。
B.父类
AbstractProcessor是这次的核心类,编译器在编译的时候会扫描这个类的子类。有一个核心方法,public boolean process,必须用子类实现。如果这个方法返回true,说明那个类编译后的抽象树的结构又发生了变化,需要重新做词法分析和语法分析。如果返回false,则表示没有变化。

C.过程方法
主要操作逻辑是:
1.获取所有由我们的MySetterGetter标记的类。
2.遍历所有类,生成类的抽象树结构。
3.操作类:
A.找出类中的所有变量。
B.设置和获取生成变量的方法。
4.返回true,表示类结构发生了变化,需要重新解析。如果为false,则表示没有改变,所以不需要再次解析。
D.操作JCTree树
主要是在抽象树的操作上,可以查看文末附的文章进行学习。
E.方法名称拼接
这和字符串拼接没什么区别。用过反射的同学应该也知道这个操作。
至此,我们已经介绍了龙目岛的原理。怎么样?很简单。接下来,就让我们付诸行动,投入实战吧。
F.操作
最后,让我们看看如何正确运行这个工具。
1.环境
我的系统环境是macOs Monterey;
Java版本是
openjdk版本" 1.8.0_302"OpenJDK运行时环境OpenJDK 64位服务器虚拟机
2.编译处理器
在存储两个类的目录中编译,MySetterGetter和MySetterGetterProcessor。
javac-CP $ JAVA _ HOME/lib/tools . jar MySetterGetter.java MySetterGetterProcessor.java
成功执行后,会出现这三个类文件。
3.声明插件注释处理器。
在项目的资源下创建一个名为meta-INFO.services的包,然后创建一个名为javax . annotation . processing . processor的文件。我的配置如下:
com . study . practice . name checker . mystergetterprocessor
4.使用我们的工具编译目标类。
比如这次我们要编《test.java》。
它的内容被再次审查:
@MySetterGetter //标记我们的标注公共类Test { Private String WZJ}
然后我们会编译它。
javac-processor com . study . practice . name checker . my settergetterprocessor com/study/practice/name checker/test . Java
执行后,如果我的代码没有被修改,这些字符串将被打印:
1流程2注意:wzj已被处理流程1
最后,将生成Test.class文件。
5.结果
最后一个类文件是这样解析出来的。如下图所示:
看到Setter/Getter方法意味着我们完成了!不是很简单。
到目前为止,我们已经学会了如何编写自己的简单Lombok插件。
附件

treemarker简介:
http://www . doc jar . com/docs/API/com/sun/tools/javac/tree/tree maker . html
作者|王在军
原文链接:https://click.aliyun.com/m/1000353148/
本文为阿里云原创内容,未经允许不得转载。


