当前位置:首页 >  市场动态  > 正文

世界信息:学习笔记2:JavaParser-结构化Java代码解析修改器

时间:2023-04-06 21:17:07     来源:哔哩哔哩

1.学习JavaParser的动机

我学习使用JavaParser的动机,是因为在开发项目时,面对项目重构,对每次都需要面对一大堆代码做几乎相同的修改感觉非常头疼,之前两次动不动就动员了一大波人改了几天的代码给我留下了巨量心理阴影。IDE提供的重构很多时候只能针对同一个类的Name,相同Field或者Method进行处理,而现有的项目代码经常有使用相同设计规则,调用方式相似,但是class基本完全无关的代码,对这些代码进行重构会很头疼,例如将一批类型不同但是必须分开存放的handler从一个class转存到它的一个成员对象里面,或者为一批对象添加工厂构造方法/clone方法,并且因为效率问题而不允许使用运行时反射的方法进行处理。所以我稍微找了一下找到了JavaParser这个工具。

JavaParser是一个java语言的句法分析器,可以将已有的java代码拆解为代码句法解析树,并且允许通过修改树的方式对代码进行批量修改,因此通过JavaParser可以做到获取代码的解析信息,对代码进行批量检查,修改代码结构或者内容等功能。


(资料图)

我觉得JavaParser存在的不足:使用复杂,文档不好查询,批量添加代码需要添加一大堆Statement和Expression,相对于JavaPoet算比较麻烦的东西了,所以如果需求是创建从零开始的java文件,我觉得肯定还是优先使用JavaPoet。

另外被JavaParser修改过的代码,会被强制格式化成其预设的Java格式,对于我这种几乎无条件要求大括号换行的人来说简直是邪教,但是看了源代码后发现几乎只有修改源代码重打jar包或者Override原始方法时复制粘贴其原始方法微调才能实现了。

不过相对而言,因为JavaParser使用起来相对复杂,很多时候又根本查不到别人有类似需求,找不到别人的做法,自己只能对着JavaDoc无能狂怒,虽然大体上最后还是找到了解决方法,但是我总之需要为自己记录一下JavaParser的使用经验,为未来的自己提供学习记录。

JavaParser的基础是JavaCC,一种基于java的句法解析工具,可以自定义句法解析树来对输入字符串进行解析,我读研的时候项目组正好也是使用的JavaCC开发的项目,但是当时我觉得这玩意儿文档又少又难用,吐槽了很多,还以为其实没很多人用这个功能,这次发现救我狗命的JavaParser也是基于JavaCC开发的,让我略显意外(捂脸)

2.JavaParser的导入

可以通过github下载,也可以通过maven导入。如果在git上下载似乎需要自行编译成jar包。(当然也可以顺路改代码定制化,嗯)

项目地址:https://github.com/javaparser/javaparser/

3.JavaParser的顺序遍历

JavaParser解析java文件一般提供两种方式处理解析结果,一种方式是直接按照解析树的顺序进行顺序遍历,另一种方式是通过Visitor访问者模式对需要的表达式进行访问处理。本节说明顺序遍历的方法。

JavaParser会将单个java文件解析成语法结构树,例如class下定义了哪些field和method,然后method里有哪些代码,单行代码的结构又可以细分为哪些表达式和哪些表达式的嵌套或者运算符计算等。以下代码为一个简单的示例,为指定的class文件添加一个clone方法,这个clone方法不使用默认的方法进行clone,而是会分别基于对象的final与否,创建一个new对象并将所有final对象作为构造函数输入参数,将非final参数进行赋值操作。

实际计算效率肯定不如原生native的clone方法快,这里主要是用于演示。

可以看出,核心代码片段在body.addStatement()函数,这个函数不能自由地加入string来书写java片段,而必须通过严格地按照java解析树一样的结构来加入一段java代码解析单元。所以添加一小段代码的操作可能都需要花不少功夫,查个半天javaDoc来找需要的Expr子类(我真的找了很久,文档说的也不清楚根本看不懂这些Expr都是啥意思,找到FieldAccessExpr花了老半天功夫...github的readme里提供的样例太浅不能作为参考,国内搜索净是复读机,真的让我头疼了挺久)

4. JavaParser的访问者模式遍历

上述代码中可以看到,以顺序方式访问JavaParser的解析树是一个比较麻烦的事情,如果需求修改的代码对象可以用很简单的逻辑找到,使用顺序遍历方式就要先写一堆代码有点烦。JavaParser还推出了通过访问者模式进行代码修改的方法,以下列代码为例,可以将一个指定包内的已有的obj.func()重构为obj.subObj().func()函数,通过访问者模式直接找到“函数调用”类型的表达式,直接进行修改。

访问者模式适用于原始数据结构十分固定而复杂,需要进行逻辑处理的对象经常基于原始数据结构中特定类型数据的情况。具体的设计模式可以另外查询。

另外,JavaParser的代码printer部分也使用了访问者模式,JavaParser生成java文件的代码结构是硬编码在printer方法里的,如果希望其按照自己期望的格式输出java文件(例如大括号分行党),也需要通过访问者模式继承DefaultPrettyPrinter来实现。

5.JavaParser的个人总结

JavaParser对代码可以进行解析,修改重构,输出代码结构树。严格结构的代码树可以以特定想要的逻辑进行检查或者修改,但是代价是修改成本往往比直接输出字符串要困难很多,不会的时候查个半天文档还要作测试代码才会用,维护过程也需要更多阅读才能理解。大体上我是不愿意用JavaParser从零开始生成代码的,感觉真的很烦人。与之相对应,如果有明确的对代码批量修改的需求时,JavaParser就会很救命,可以从无穷无尽的手动修改代码中解放出来,从这一点来说我是可以给好评的。

标签:

推荐文章