JavaParser⽣成,分析和修改Java代码
作为开发⼈员,我们经常鄙视⼿动进⾏重复⼯作的⼈员。
我们认为, 他们应该实现这⼀⽬标 。
尽管如此,我们还是进⾏与编码有关的所有活动。 当然,我们使⽤的⾼级IDE可以为我们执⾏⼀些重构,但这基本上就是结束了。 我们不品尝我们⾃⼰的药。
让我们改变它。 让我们看看如何将代码编写为:
⽣成我们必须编写的⽆聊的重复性Java代码
分析我们的代码以回答有关它的⼀些问题
做⼀些代码处理和重构
好消息是,我们将使⽤⼀组库来实现所有这些功能:JavaParser和它的弟弟JavaSymbolSolver。
⼊门
好吧,这很简单:只需将添加到您的依赖项中即可。
什么是JavaSymbolSolver? 它是补充库,为它提供了⼀些相当强⼤的功能,这些功能对于回答关于代码的更复杂的问题是必需的。
JavaSymbolSolver依赖于JavaParser,因此您只需要添加JavaSymbolSolver,Maven或Gradle也会为您提供JavaParser。
我假设您知道如何使⽤Maven或Gradle。 如果您不喜欢,请停⽌阅读并开始学习!
使⽤javaparser⽣成代码
在某些情况下,您可能需要⽣成Java代码。 例如,您可能想基于⼀些外部数据⽣成代码,例如数据库架构或REST API。
您可能还需要将其他语⾔翻译成Java。 例如,我设计了⽤于⽣活的DSL,⽽当⽤户只能看到我为他们构建的DSL时,我经常在后台⽣成Java并将其编译。
有时候,您只想⽣成样板代码,就像我以前在使⽤JavaEE和所有这些层(谁能记住编写EJB的过程很⽆聊?)时使⽤dp⼀样。
⽆论⽣成代码的原因是什么,都可以使⽤JavaParser。 JavaParser不会提出问题,它只是在帮助您。
让我们看看如何⽣成⼀个具有两个字段的类,⼀个构造函数和两个getter。 没什么特别先进的,但是它应该使您了解使⽤JavaParser进⾏代码⽣成的含义。
CompilationUnit cu = new CompilationUnit();
cu.setPackageDeclaration("del");
ClassOrInterfaceDeclaration book = cu.addClass("Book");
book.addField("String", "title");
book.addField("Person", "author");
book.addConstructor(Modifier.PUBLIC)
.addParameter("String", "title")
.addParameter("Person", "author")
.setBody(new BlockStmt()
.addStatement(new ExpressionStmt(new AssignExpr(
new FieldAccessExpr(new ThisExpr(), "title"),
new NameExpr("title"),
AssignExpr.Operator.ASSIGN)))
.addStatement(new ExpressionStmt(new AssignExpr(
new FieldAccessExpr(new ThisExpr(), "author"),
new NameExpr("author"),
AssignExpr.Operator.ASSIGN))));
book.addMethod("getTitle", Modifier.PUBLIC).setBody(
new BlockStmt().addStatement(new ReturnStmt(new NameExpr("title"))));
book.addMethod("getAuthor", Modifier.PUBLIC).setBody(
new BlockStmt().addStatement(new ReturnStmt(new NameExpr("author"))));
System.out.String());
最后⼀条指令将打印出您的代码,并且可以⽴即进⾏编译。 您可能希望将代码保存到⽂件中⽽不是打印它,但是您明⽩了。使⽤javaparser分析代码
您可能会询问有关代码的许多不同问题,以及许多不同的分析⽅式。
⾸先,让我们解析项⽬的所有源⽂件:
// Parse all source files
SourceRoot sourceRoot = new Path());
sourceRoot.setParserConfiguration(parserConfiguration);
List<ParseResult> parseResults = ToParse("");
// Now get all compilation unitsList
allCus = parseResults.stream()
.filter(ParseResult::isSuccessful)
.map(r -> r.getResult().get())
.List());
我们还创建⼀个⽅法来获取所有编译单元中特定类型的所有节点:
public static  List getNodes(List cus, Class nodeClass) {
List res = new LinkedList();
cus.forEach(cu -> res.addAll(cu.findAll(nodeClass)));
return res;
}
然后让我们开始提问,例如:
有多少种⽅法采⽤3个以上的参数?
long n = getNodes(allCus, MethodDeclaration.class)        .stream()        .filter(m -> m.getParameters().size() > 3)
.count();System.out.println("N of methods with 3+ params: " + n);
⼤多数⽅法中的三个顶级类别是什么?
getNodes(allCus, ClassOrInterfaceDeclaration.class)        .stream()        .filter(c -> !c.isInterface())        .sorted(ComparatorparingInt(o ->
-1 * o.getMethods().size()))        .limit(3)        .forEach(c ->
System.out.NameAsString() + ": " +            c.getMethods().size() + " methods"));
好的,您知道了。 现在去检查您的代码。 您没有什么可隐藏的,对吗?
使⽤javaparser转换代码
假设您是某个库的满意⽤户。 ⼏年前,您已将其添加到依赖项中,并从此以后就愉快地使⽤它。 时间已经过去,您已经在整个项⽬中越来越多地使⽤它。
有⼀天,该有⽤库的新版本出现了,您决定要更新依赖项。 现在,他们在新库中删除了您正在使⽤的⽅法之⼀。 确保已弃⽤它,并将其命名为oldMethod (可能告诉您⼀些信息……)。
现在oldMethod已被newMethod取代。 newMethod具有3个参数:前两个参数与oldMethod相同,只是将它们取反,第三个参数是布尔值,应将其设置为true以获得与oldMethod相同的⾏为。
您有对oldMethod的数百个调⽤…是否要⼀个⼀个地更改它们? 好吧,也许,如果您按⼩时收费。 或者,您可以只使⽤JavaParser代替。
⾸先,让我们在某个⽂件(即JavaParser parlanse中的CompilationUnit)中到对旧⽅法的所有调⽤:
myCompilationUnit.findAll(ethodCallExpr.class)
.stream()
.filter(m -> m.resolveInvokedMethod()
.getQualifiedSignature()
.equals("foo.MyClass.oldMethod(java.lang.String, int)"))
.forEach(m -> m.replace(replaceCallsToOldMethod(m)));javaparser野外
然后,让我们将旧调⽤转换为新调⽤:
public MethodCallExpr replaceCallsToOldMethod(MethodCallExpr methodCall) {
MethodCallExpr newMethodCall = new MethodCallExpr(
newMethodCall.Argument(1));
newMethodCall.Argument(0));
newMethodCall.addArgument(new BooleanLiteralExpr(true));
return newMethodCall;
}
太酷了,现在我们只需要获取修改后的CompilationUnit的代码并将其保存到Java⽂件即可。
newMethod使⽤寿命长 !
在哪⾥可以到有关javaparser的更多信息
我们还没有看到JavaParser的众多功能:
JavaParser可以处理注释,弄清楚它们所引⽤的元素
JavaParser可以进⾏词法保留或漂亮的打印 :您的选择
它可以出⼀个⽅法调⽤指向哪个⽅法声明,某个类具有哪个祖先,以及更多地归功于与JavaSymbolSolver的集成。
它可以将AST导出为JSON,XML,YAML,甚⾄可以使⽤Graphviz⽣成图表!
您在哪⾥可以了解所有这些东西?
这⾥有⼀些资源:
我们写了⼀本关于JavaParser和JavaSymbolSolver的书,可免费获得。 它被命名为
Matozoid伟⼤的 :他是JavaParser的光荣维护者,这是不可阻挡的⼒量,每隔⼀个星期就会推出新版本。 谁更了解JavaParser?
我拙劣 。 我是JavaSymbolSolver的维护者,我尝试作为JavaParser中的第⼆命令来提供帮助。 遥远的第⼆个&#55357;&#56898;
该的 :⽬前内容还不是很丰富,但是我们正在努⼒
:您有问题吗? 在那⼉问他们
摘要
⼏乎没有情况可以学习如何使⽤⼀种⼯具来完成三件不同的事情。 通过学习如何使⽤JavaParser,您可以分析,⽣成和修改Java代码。好吧,感觉就像圣诞节,不是吗?