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源代碼 的材料整頓,後續持續彌補相干材料,感謝年夜家對本站的支撐!