Java Parser介绍
[toc]
Java Parser基本信息
JavaParser是一个Java库,用于将Java源代码解析为抽象语法树(AST),在AST基础上进行类型推断分析,支持修改AST从而生成新的Java文件内容(解析、修改和生成 Java 代码)。官网:https://javaparser.org
通过使用JavaParser,你可以执行以下任务:
- 分析Java源代码并获取详细信息。
- 修改现有的Java代码,并生成新的Java源代码。
- 自动化代码转换和重构任务。
- 构建静态代码分析工具。
JavaParser的特点
- 基于ANTLR 4的高性能解析器。ANTLR 4是一个流行的词法分析器和语法分析器生成器,用于构建语言、工具和服务。
- 完全符合Java语法规则。JavaParser遵循最新的Java语言规范,支持Java 8至17的所有版本。
- 简洁易用的API。JavaParser提供了清晰、直观的API,使您可以轻松地在Java代码上工作。
- 广泛的生态系统。JavaParser与其他流行的技术(如Junit、Mockito和Checkstyle)兼容,拥有丰富的生态系统。
- 开源免费。JavaParser是根据Apache 2.0许可证发布的,可以自由使用和扩展。
核心组件
JavaParser 的主要构成由以下几个组件组成:
Lexer(词法分析器):词法分析器的作用是读取源代码文本,并将其分解成一系列的词法单元(tokens),如关键字、标识符、字面量、运算符等。这是解析过程的第一步。

通常不需要显式调用,JavaParser将具体的细节实现隐藏在内部,调用方只需要使用api即可完成源码到AST的转换。具体可以翻阅com.github.javaparser.GeneratedJavaParserParser(语法解析器):语法分析器接收词法分析器生成的tokens,并根据Java语言的语法规则将它们组合成各种语法结构,如表达式、语句、类定义等。这个过程构建出一个抽象语法树(AST)。
com.github.javaparser.JavaParser这是最常用的类。AST(抽象语法树):AST 是 JavaParser 的核心数据结构,它以层次化的方式表示了源代码的结构。AST 由一系列的节点组成,每个节点表示源代码中的一个元素,如类、方法、字段、表达式等。每个节点都包含有关该元素的信息,例如名称、类型、修饰符等。
AST是后续操作(如遍历、分析、修改)的基础,也是使用方操作最频繁的类。
com.github.javaparser.ast.CompilationUnit是一个非常重要的类,它代表了Java源代码文件的根节点,是这个结构的抽象表示,包含整个文件的结构,例如:包声明(Package Declaration)
导入声明(Imports)
类型声明(Type Declarations),这可能是类、接口、枚举或注解
注释(Comments)
任何顶级的注解
通过操作CompilationUnit提供的公有方法,可以访问和修改文件中的元素。包括:
获取和设置包声明
获取和添加导入声明
获取和添加类型声明
获取和添加注释
使用访问者模式来遍历AST中的节点

Visitors(访问器):顾名思义,这是一个采用访问者模式设计的组件,可以用于遍历和操作 AST 。开发者可以编写自定义的 Visitors,通过遍历 AST 来访问特定类型的节点,执行代码分析、重构、生成等任务。
com.github.javaparser.ast.visitor.GenericVisitor和com.github.javaparser.ast.visitor.VoidVisitor这两个访问器提供了默认实现,如果需要自定义访问器,可以继承它们来实现自己的业务逻辑。Printer(打印器):Printer 用于将 AST 转换回 Java 源代码的字符串表示形式。它可以将修改后的 AST 打印回原始源代码文件,或将 AST 打印为格式化的代码字符串。
核心类
CompilationUnit
Javaparser解析java代码后会生成AST(abstract syntax tree,抽象语法树),CompilationUnit(编译单元)是每个java文件被解析后直接生成的对象,是读取和操作java文件的入口。CompilationUnit包含了整棵AST的Node,可以理解为AST的根节点。

上图根据代码画出了CompilationUnit的结构中包含了三个子节点,一个package申明,一个import申明,一个类定义。上图并没有完整的描述整个语法树,绿色三角形的部分被省略了,下图展示了省略的MethodDeclatation部分:

通过其四个节点,我们可看出其返回类型是void,方法名是main,方法参数是String args,以及其方法体:

Node
AST的Node,对于java中的类、接口、注解、方法、入参、赋值语句、注释、if条件、注释等都是一种Node,如果Node表示的代码块能继续细化分割,就在其子节点列表NodeList中,Node是读取和操作AST的基本单元。
Node的部分子类:

这些Node类和其表示的java代码(每个类的注释中有,官方文档附录B中有全部的Node类及其示例)

Visitor
Javaparser使用访问者模式来访问或修改Node,当需要修改Node时,Node本身不需要额外增加方法,而是通过创建一个Visitor,在Visitor中定义好需要修改什么,用Node调用方法接收我们创建的Visitor完成修改。
Visitable和两类Visitor接口
为了实现这种模式,作者设计了两类接口,一个是Visitable,一个是Visitor(根据有无返回值,分为GenericVisitor和VoidVisitor)。
(1)所有可访问的Node都实现了Visitable接口,这个接口有两个accept方法,用于接收Visitor以及外部参数arg,外部参数可用于收集遍历到的东西。
(2)所有Node实现Visitable接口的方法都是传递自身实例和外部arg。
(3)针对不同类型Node,实现GenericVisitor接口中对应的方法就能访问这种类型的Node。

也就是说我们用Node实例调用它的accept方法,传入一个编写好的Visitor,Visitor中的实现方法就能访问这个Node。
VoidVisitorAdapter
当我们只需要访问java代码而不需要做修改时,直接继承VoidVisitorAdapter。这个抽象类对VoidVisitor做了默认实现,通过递归执行accept方法来达到遍历整个AST的目的。

上图中对CompilationUnit的import(引包)、module(java高版本模块)、package(所属包)、type(定义的类型class、interface、enum、annotation)、comment(注释)分别遍历执行accept,而其中的每一种Node又会遍历子节点执行accept。
例如下图访问class和interface的ClassOrInterfaceDeclaration的方法,分别遍历它涵盖的节点。

ModifierVisitor
与VoidVisitorAdapter不同的是,ModifierVisitor继承带返回值的GenericVisitor,其返回值用于返回修改后的节点。如下图所示,遍历各项子节点以后,将返回值作为修改后的对象重新赋值。

参考:
https://cloud.tencent.com/developer/news/740798
https://blog.csdn.net/gitblog_00089/article/details/136705307
https://blog.csdn.net/qq_40800602/article/details/134446052
https://blog.csdn.net/JDDTechTalk/article/details/136053986