JDK的Parser來解析Java源代碼詳解。本站提示廣大學習愛好者:(JDK的Parser來解析Java源代碼詳解)文章只能為提供參考,不一定能成為您想要的結果。以下是JDK的Parser來解析Java源代碼詳解正文
在JDK中,自帶了一套相干的編譯API,可以在Java中提議編譯流程,解析Java源文件然後獲得其語法樹,在JDK的tools.jar(OSX下可以在/Library/Java/JavaVirtualMachines/jdk_version/Contents/Home/lib中找到)中包括著這整套API,然則這卻不是Oracle和OpenJDK宣布中的地下API,是以關於這套API,並沒有官方的正式文檔來停止解釋。然則,也有很多項目應用了這套API來做了很多工作,例如年夜名鼎鼎的lombok應用了這套API在Annotation Processing階段修正了源代碼中的語法樹,終究成果相當於直接在源文件中拔出了新的代碼!
因為這套API今朝缺乏相干文檔,應用起來比擬艱苦,例如,解析源代碼中的一切變量,並打印出來:
public class JavaParser { private static final String path = "User.java"; private JavacFileManager fileManager; private JavacTool javacTool; public JavaParser() { Context context = new Context(); fileManager = new JavacFileManager(context, true, Charset.defaultCharset()); javacTool = new JavacTool(); } public void parseJavaFiles() { Iterable<!--? extends JavaFileObject--> files = fileManager.getJavaFileObjects(path); JavaCompiler.CompilationTask compilationTask = javacTool.getTask(null, fileManager, null, null, null, files); JavacTask javacTask = (JavacTask) compilationTask; try { Iterable<!--? extends CompilationUnitTree--> result = javacTask.parse(); for (CompilationUnitTree tree : result) { tree.accept(new SourceVisitor(), null); } } catch (IOException e) { e.printStackTrace(); } } static class SourceVisitor extends TreeScanner<void, void=""> { private String currentPackageName = null; @Override public Void visitCompilationUnit(CompilationUnitTree node, Void aVoid) { return super.visitCompilationUnit(node, aVoid); } @Override public Void visitVariable(VariableTree node, Void aVoid) { formatPtrln("variable name: %s, type: %s, kind: %s, package: %s", node.getName(), node.getType(), node.getKind(), currentPackageName); return null; } } public static void formatPtrln(String format, Object... args) { System.out.println(String.format(format, args)); } public static void main(String[] args) { new JavaParser().parseJavaFiles(); } }</void,>
個中 User.java的代碼以下:
package com.ragnarok.javaparser; import com.sun.istack.internal.Nullable; import java.lang.Override; public class User { @Nullable private String foo = "123123"; private Foo a; public void UserMethod() {} static class Foo { private String fooString = "123123"; public void FooMethod() {} } }
履行下面的JavaParser成果以下:
variable: foo, annotaion: Nullable variable name: foo, type: String, kind: VARIABLE, package: com.ragnarok.javaparser variable name: a, type: Foo, kind: VARIABLE, package: com.ragnarok.javaparser
這裡我們是起首經由過程JavaCompiler.CompilationTask解析了源文件以後,再應用自界說的SourceVisitor(繼續自TreeScanner)來對源代碼的構造停止拜訪,在SourceVisitor類中,經由過程重載visitVariable來對一個編譯單位(單個源代碼文件)停止解析,拜訪個中的一切的變量,這裡可以看出,我們沒有方法拿到這個變量類型的全限制名(包括包名),只能拿到的對應的簡略名字,是以,類型切實其實定須要內部完成自行肯定,例如可以經由過程記載類地點的包名,遞歸的搜刮全部源代碼目次來跟蹤一切類的全限制名,查找import中能否包括對應的類型等。
TreeScanner中除visitVariable辦法外,還包括了年夜量其他的visitXYZ辦法,例如,可以遍歷一切的import,辦法界說,Annotation等,更詳細可以檢查OpenJDK中關於這個的源代碼
這裡再來看下別的一個例子,重載visitClass辦法,拜訪一切的外部類和類自己:
@Override public Void visitClass(ClassTree node, Void aVoid) { formatPtrln("class name: %s", node.getSimpleName()); for (Tree member : node.getMembers()) { if (member instanceof VariableTree) { VariableTree variable = (VariableTree) member; List<!--? extends AnnotationTree--> annotations = variable.getModifiers().getAnnotations(); if (annotations.size() > 0) { formatPtrln("variable: %s, annotaion: %s", variable.getName(), annotations.get(0).getAnnotationType()); } else { formatPtrln("variable: %s", variable.getName()); } } } return super.visitClass(node, aVoid); }
這裡簡略的打印了類名和變量的稱號,類型,annotation類型,履行下面的代碼,成果以下:
class name: User variable: foo, annotaion: Nullable variable: a class name: Foo variable: fooString
可以看出我們把類名和類中的變量都打印了出來。而在visitClass辦法中,我們可以經由過程getMembers辦法拿到類中一切的成員,包含變量,辦法,annotation等,分離對應著分歧的類型,例如變量就對應著VariableTree類型,辦法就對應的MethodTree類型。
總得來講,固然現實上應用其實不算特殊龐雜,然則因為缺乏文檔,對應用形成了很年夜的妨礙,並且今朝所引見的只是這套API的一少部門,後續我將會持續研討這套API的相干函數。
以上就是對JDK的Parser來解析Java源代碼 的材料整頓,後續持續彌補相干材料,感謝年夜家對本站的支撐!