用了那么久的,你知道它的原理么

核心提示序言在写Java代码的时候,最烦写setter/getter方法,自从有了Lombok插件不用再写那些方法之后,感觉再也回不去了,那你们是否好奇过Lombok是怎么把setter/getter方法给你加上去的呢?有的同学说我们Java引入L

前言

写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/

本文为阿里云原创内容,未经允许不得转载。

 
友情链接
鄂ICP备19019357号-22