【问题标题】:Accessing source code from Java Annotation Processor从 Java 注释处理器访问源代码
【发布时间】:2011-06-16 14:01:22
【问题描述】:

我正在尝试从 Java 注释处理器中访问某个类型的实际原始源代码。这有可能吗?谢谢!

【问题讨论】:

    标签: java annotations annotation-processing


    【解决方案1】:

    我遇到了一个问题,我必须访问一些源代码(非字符串/非原始常量的初始化代码),并通过Compiler Tree 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();
    

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

    如需更多阅读和示例,请阅读此article: Source Code Analysis Using Java 6 APIs

    【讨论】:

      【解决方案2】:

      @AdrianoNobre 的answer 的简单改编。它更好地反映了访问者模式的预期用途。

      抽象处理器初始化:

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

      方法扫描器

      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();
      

      【讨论】:

      • methodTree.getBody() 有空的语句列表。我正在尝试按原样阅读该方法的主体。有可能吗?
      【解决方案3】:

      快速回答是不可能的。

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

      Mirror API 用于建模 程序的语义结构。它 提供的表示 在程序中声明的实体,例如 作为类、方法和字段。 在方法级别以下构造, 例如个人陈述和 表达式,没有表示

      Java 6 注释处理基于 new API,但它仍然没有提供有关代码结构的更多详细信息。

      【讨论】:

        【解决方案4】:

        镜像 API 等效于反射 API,但在编译时。无法使用此 API 读取方法的内部内容。其他都应该没问题。

        如果您真的想这样做,那么可能会有一些技巧来获取您要读取的源文件的输入流。

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

        • 另一种解决方案是找出源文件的路径,然后打开常规输入流。我为 AndroidAnnotations 做了这种肮脏的 hack,在编译时读取 AndroidManifest.xml 文件。见AndroidManifestFinder.findManifestFile()

        【讨论】:

        • 我对第二个解决方案有疑问。如何找出源文件的路径,其中某个类已使用我的自定义注释进行了注释?您提供的链接实际上已损坏。
        【解决方案5】:

        您可以尝试编译器树 API (http://download.oracle.com/javase/6/docs/jdk/api/javac/tree/index.html)
        java编译器使用这个API来处理java程序的抽象语法树。它甚至涉及到 Java 语言结构,例如语句、循环、表达式等。 你可以在你的 JDK 目录中找到 jar 库(名为 tools.jar)

        【讨论】:

          猜你喜欢
          • 2013-01-27
          • 2011-04-14
          • 1970-01-01
          • 2015-01-09
          • 1970-01-01
          • 1970-01-01
          • 2012-11-21
          • 2010-09-24
          • 1970-01-01
          相关资源
          最近更新 更多