从Java Annotation Processor访问源代码

我试图从Java Annotation Processor中访问类型的实际原始源代码。 这有可能吗? 谢谢!

我有一个问题,我必须访问一些源代码(非String /非原始常量的初始化代码),并通过编译器树API访问源代码解决它。

这是一般食谱:

1.创建自定义TreePathScanner:

private static class CodeAnalyzerTreeScanner extends TreePathScanner { private String fieldName; private String fieldInitializer; public void setFieldName(String fieldName) { this.fieldName = fieldName; } public String getFieldInitializer() { return this.fieldInitializer; } @Override public Object visitVariable(VariableTree variableTree, Trees trees) { if (variableTree.getName().toString().equals(this.fieldName)) { this.fieldInitializer = variableTree.getInitializer().toString(); } return super.visitVariable(variableTree, trees); } 

2.在AbstractProcessor中,通过覆盖init方法保存对当前编译树的引用:

 @Override public void init(ProcessingEnvironment pe) { super.init(pe); this.trees = Trees.instance(pe); } 

3.获取VariableElement的初始化源代码(在您的情况下为枚举):

 // assuming theClass is a javax.lang.model.element.Element reference // assuming theField is a javax.lang.model.element.VariableElement reference String fieldName = theField.getSimpleName().toString(); CodeAnalyzerTreeScanner codeScanner = new CodeAnalyzerTreeScanner(); TreePath tp = this.trees.getPath(theClass); codeScanner.setFieldName(fieldName); codeScanner.scan(tp, this.trees); String fieldInitializer = codeScanner.getFieldInitializer(); 

就是这样! 最后,fieldInitiliazer变量将包含用于初始化常量的精确代码行。 通过一些调整,您应该能够使用相同的配方来访问源代码树中其他元素类型的源代码(即方法,包声明等)

有关更多阅读和示例,请阅读本文:使用Java 6 API进行源代码分析 。

Mirror API与Reflection API等效,但是在编译时。 无法使用此API读取方法的内部内容。 其他任何事都应该没问题。

如果你真的想这样做,那么可能会有黑客在你想要阅读的源文件上获得输入流。

  • Hibernate Metamodel Generator使用Filer.getResource()在XmlParser.getInputStreamForResource()中读取XML文件。 问题是只支持CLASS_OUTPUT和SOURCE_OUPUT,因此它可能不适合您。

  • 另一个解决方案涉及找出源文件的路径,然后只打开常规输入流。 我已经为AndroidAnnotations做了这种肮脏的黑客攻击,在编译时读取AndroidManifest.xml文件。 请参阅AndroidManifestFinder.findManifestFile() 。

简单地改编了@AdrianoNobre的答案 。 它更好地反映了访客模式的预期用途。

AbstractProcessor init:

 private Trees trees; @Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); trees = Trees.instance(processingEnv); } 

MethodScanner

 private static class MethodScanner extends TreePathScanner, Trees> { private List methodTrees = new ArrayList<>(); public MethodTree scan(ExecutableElement methodElement, Trees trees) { assert methodElement.getKind() == ElementKind.METHOD; List methodTrees = this.scan(trees.getPath(methodElement), trees); assert methodTrees.size() == 1; return methodTrees.get(0); } @Override public List scan(TreePath treePath, Trees trees) { super.scan(treePath, trees); return this.methodTrees; } @Override public List visitMethod(MethodTree methodTree, Trees trees) { this.methodTrees.add(methodTree); return super.visitMethod(methodTree, trees); } } 

使用扫描仪获取方法体:

 MethodScanner methodScanner = new MethodScanner(); MethodTree methodTree = methodScanner.scan(methodElement, this.trees); methodTree.getBody(); 

快速回答是,这是不可能的。

来自Sun SDK 4中的注释处理中使用的Mirror API JavaDoc :

Mirror API用于模拟程序的语义结构。 它提供了在程序中声明的实体的表示,例如类,方法和字段。 不表示方法级别下的构造,例如单个语句和表达式。

Java 6 Annotation Processing基于一个新的API ,但它仍然没有提供有关代码结构的更多细节。

您可以尝试Compiler Tree API( http://download.oracle.com/javase/6/docs/jdk/api/javac/tree/index.html
java编译器使用此API来处理java程序的抽象语法树。 就Java语言构造(如语句,循环,表达式等)而言,您可以在JDK目录中找到jar库(名为tools.jar)