繁体   English   中英

从Java Annotation Processor访问源代码

[英]Accessing source code from Java Annotation Processor

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

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

这是一般食谱:

1.创建自定义TreePathScanner:

private static class CodeAnalyzerTreeScanner extends TreePathScanner<Object, Trees> {

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进行源代码分析

简单改编@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<List<MethodTree>, Trees> {
    private List<MethodTree> methodTrees = new ArrayList<>();

    public MethodTree scan(ExecutableElement methodElement, Trees trees) {
        assert methodElement.getKind() == ElementKind.METHOD;

        List<MethodTree> methodTrees = this.scan(trees.getPath(methodElement), trees);
        assert methodTrees.size() == 1;

        return methodTrees.get(0);
    }

    @Override
    public List<MethodTree> scan(TreePath treePath, Trees trees) {
        super.scan(treePath, trees);
        return this.methodTrees;
    }

    @Override
    public List<MethodTree> 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();

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

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

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

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

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

来自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)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM