JavaParser生成,分析和修改Java代码
作為開(kāi)發(fā)人員,我們經(jīng)常鄙視手動(dòng)進(jìn)行重復(fù)工作的人員。
我們認(rèn)為, 他們應(yīng)該實(shí)現(xiàn)這一目標(biāo) 。
盡管如此,我們還是進(jìn)行與編碼有關(guān)的所有活動(dòng)。 當(dāng)然,我們使用的高級(jí)IDE可以為我們執(zhí)行一些重構(gòu),但這基本上就是結(jié)束了。 我們不品嘗我們自己的藥。
讓我們改變它。 讓我們看看如何將代碼編寫為:
- 生成我們必須編寫的無(wú)聊的重復(fù)性Java代碼
- 分析我們的代碼以回答有關(guān)它的一些問(wèn)題
- 做一些代碼處理和重構(gòu)
好消息是,我們將使用一組庫(kù)來(lái)實(shí)現(xiàn)所有這些功能:JavaParser和它的弟弟JavaSymbolSolver。
入門
好吧,這很簡(jiǎn)單:只需將JavaSymbolSolver添加到您的依賴項(xiàng)中即可。
什么是JavaSymbolSolver? 它是JavaParser的補(bǔ)充庫(kù),為它提供了一些相當(dāng)強(qiáng)大的功能,這些功能對(duì)于回答關(guān)于代碼的更復(fù)雜的問(wèn)題是必需的。
JavaSymbolSolver依賴于JavaParser,因此您只需要添加JavaSymbolSolver,Maven或Gradle也會(huì)為您提供JavaParser。
我假設(shè)您知道如何使用Maven或Gradle。 如果您不喜歡,請(qǐng)停止閱讀并開(kāi)始學(xué)習(xí)!
使用javaparser生成代碼
在某些情況下,您可能需要生成Java代碼。 例如,您可能想基于一些外部數(shù)據(jù)生成代碼,例如數(shù)據(jù)庫(kù)架構(gòu)或REST API。
您可能還需要將其他語(yǔ)言翻譯成Java。 例如,我設(shè)計(jì)了用于生活的DSL,而當(dāng)用戶只能看到我為他們構(gòu)建的DSL時(shí),我經(jīng)常在后臺(tái)生成Java并將其編譯。
有時(shí)候,您只想生成樣板代碼,就像我以前在使用JavaEE和所有這些層(誰(shuí)能記住編寫EJB的過(guò)程很無(wú)聊?)時(shí)使用dp一樣。
無(wú)論生成代碼的原因是什么,都可以使用JavaParser。 JavaParser不會(huì)提出問(wèn)題,它只是在幫助您。
讓我們看看如何生成一個(gè)具有兩個(gè)字段的類,一個(gè)構(gòu)造函數(shù)和兩個(gè)getter。 沒(méi)什么特別先進(jìn)的,但是它應(yīng)該使您了解使用JavaParser進(jìn)行代碼生成的含義。
CompilationUnit cu = new CompilationUnit();cu.setPackageDeclaration("jpexample.model");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.println(cu.toString());最后一條指令將打印出您的代碼,并且可以立即進(jìn)行編譯。 您可能希望將代碼保存到文件中而不是打印它,但是您明白了。
使用javaparser分析代碼
您可能會(huì)詢問(wèn)有關(guān)代碼的許多不同問(wèn)題,以及許多不同的分析方式。
首先,讓我們解析項(xiàng)目的所有源文件:
// Parse all source files SourceRoot sourceRoot = new SourceRoot(myProjectSourceDir.toPath()); sourceRoot.setParserConfiguration(parserConfiguration); List<ParseResult> parseResults = sourceRoot.tryToParse("");// Now get all compilation unitsList allCus = parseResults.stream() .filter(ParseResult::isSuccessful) .map(r -> r.getResult().get()) .collect(Collectors.toList());我們還創(chuàng)建一個(gè)方法來(lái)獲取所有編譯單元中特定類型的所有節(jié)點(diǎn):
public static List getNodes(List cus, Class nodeClass) {List res = new LinkedList();cus.forEach(cu -> res.addAll(cu.findAll(nodeClass)));return res; }然后讓我們開(kāi)始提問(wèn),例如:
有多少種方法采用3個(gè)以上的參數(shù)?
long n = getNodes(allCus, MethodDeclaration.class) .stream() .filter(m -> m.getParameters().size() > 3).count();System.out.println("N of methods with 3+ params: " + n);大多數(shù)方法中的三個(gè)頂級(jí)類別是什么?
getNodes(allCus, ClassOrInterfaceDeclaration.class) .stream() .filter(c -> !c.isInterface()) .sorted(Comparator.comparingInt(o -> -1 * o.getMethods().size())) .limit(3) .forEach(c -> System.out.println(c.getNameAsString() + ": " + c.getMethods().size() + " methods"));好的,您知道了。 現(xiàn)在去檢查您的代碼。 您沒(méi)有什么可隱藏的,對(duì)嗎?
使用javaparser轉(zhuǎn)換代碼
假設(shè)您是某個(gè)庫(kù)的滿意用戶。 幾年前,您已將其添加到依賴項(xiàng)中,并從此以后就愉快地使用它。 時(shí)間已經(jīng)過(guò)去,您已經(jīng)在整個(gè)項(xiàng)目中越來(lái)越多地使用它。
有一天,該有用庫(kù)的新版本出現(xiàn)了,您決定要更新依賴項(xiàng)。 現(xiàn)在,他們?cè)谛聨?kù)中刪除了您正在使用的方法之一。 確保已棄用它,并將其命名為oldMethod (可能告訴您一些信息……)。
現(xiàn)在oldMethod已被newMethod取代。 newMethod具有3個(gè)參數(shù):前兩個(gè)參數(shù)與oldMethod相同,只是將它們?nèi)》?#xff0c;第三個(gè)參數(shù)是布爾值,應(yīng)將其設(shè)置為true以獲得與oldMethod相同的行為。
您有對(duì)oldMethod的數(shù)百個(gè)調(diào)用…是否要一個(gè)一個(gè)地更改它們? 好吧,也許,如果您按小時(shí)收費(fèi)。 或者,您可以只使用JavaParser代替。
首先,讓我們?cè)谀硞€(gè)文件(即JavaParser parlanse中的CompilationUnit)中找到對(duì)舊方法的所有調(diào)用:
myCompilationUnit.findAll(ethodCallExpr.class).stream().filter(m -> m.resolveInvokedMethod() .getQualifiedSignature() .equals("foo.MyClass.oldMethod(java.lang.String, int)")) .forEach(m -> m.replace(replaceCallsToOldMethod(m)));然后,讓我們將舊調(diào)用轉(zhuǎn)換為新調(diào)用:
public MethodCallExpr replaceCallsToOldMethod(MethodCallExpr methodCall) { MethodCallExpr newMethodCall = new MethodCallExpr(methodCall.getScope().get(), "newMethod"); newMethodCall.addArgument(methodCall.getArgument(1)); newMethodCall.addArgument(methodCall.getArgument(0)); newMethodCall.addArgument(new BooleanLiteralExpr(true)); return newMethodCall; }太酷了,現(xiàn)在我們只需要獲取修改后的CompilationUnit的代碼并將其保存到Java文件即可。
newMethod使用壽命長(zhǎng) !
在哪里可以找到有關(guān)javaparser的更多信息
我們還沒(méi)有看到JavaParser的眾多功能:
- JavaParser可以處理注釋,弄清楚它們所引用的元素
- JavaParser可以進(jìn)行詞法保留或漂亮的打印 :您的選擇
- 它可以找出一個(gè)方法調(diào)用指向哪個(gè)方法聲明,某個(gè)類具有哪個(gè)祖先,以及更多地歸功于與JavaSymbolSolver的集成。
- 它可以將AST導(dǎo)出為JSON,XML,YAML,甚至可以使用Graphviz生成圖表!
您在哪里可以了解所有這些東西?
這里有一些資源:
- 我們寫了一本關(guān)于JavaParser和JavaSymbolSolver的書(shū),可免費(fèi)獲得。 它被命名為JavaParser:Visited
- Matozoid偉大的博客 :他是JavaParser的光榮維護(hù)者,這是不可阻擋的力量,每隔一個(gè)星期就會(huì)推出新版本。 誰(shuí)更了解JavaParser?
- 我關(guān)于語(yǔ)言工程的拙劣博客 。 我是JavaSymbolSolver的維護(hù)者,我嘗試作為JavaParser中的第二命令來(lái)提供幫助。 遙遠(yuǎn)的第二個(gè)&#55357;&#56898;
- 該項(xiàng)目的網(wǎng)站 :目前內(nèi)容還不是很豐富,但是我們正在努力
- 煩惱頻道 :您有問(wèn)題嗎? 在那兒?jiǎn)査麄?
摘要
幾乎沒(méi)有情況可以學(xué)習(xí)如何使用一種工具來(lái)完成三件不同的事情。 通過(guò)學(xué)習(xí)如何使用JavaParser,您可以分析,生成和修改Java代碼。
好吧,感覺(jué)就像圣誕節(jié),不是嗎?
翻譯自: https://www.javacodegeeks.com/2017/12/javaparser-generate-analyze-modify-java-code.html
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的JavaParser生成,分析和修改Java代码的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 源源不断的近义词是什么 源源不断的近义词
- 下一篇: 功能Java示例 第2部分–讲故事