Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/334.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/list/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java AnnotationProcessor使用多个源文件创建一个文件_Java_Annotation Processing - Fatal编程技术网

Java AnnotationProcessor使用多个源文件创建一个文件

Java AnnotationProcessor使用多个源文件创建一个文件,java,annotation-processing,Java,Annotation Processing,我有两个带方法的类,我想把这两个类的方法组合成一个类 @Service("ITestService") public interface ITest1 { @Export void method1(); } @Service("ITestService") public interface ITest2 { @Export void method2(); } 结果应该是: public interface ITestService extends Remote {

我有两个带方法的类,我想把这两个类的方法组合成一个类

@Service("ITestService")
public interface ITest1
{
   @Export
   void method1();
}

@Service("ITestService")
public interface ITest2
{
   @Export
   void method2();
}
结果应该是:

public interface ITestService extends Remote
{
  void method1();
  void method2();
}
我的AnnotationProcessor的第一次运行生成正确的输出(因为RoundEnvironment包含这两个类)

但是如果我编辑其中一个类(例如添加一个新方法),RoundEnvironment只包含编辑过的类,因此结果如下(将newMethod()添加到接口ITest1)

现在缺少方法2。我不知道如何解决我的问题。是否有方法(环境)访问项目中的所有类?还是有别的办法解决这个问题?

生成该类的代码相当长,因此这里简单介绍一下如何生成该类。我使用
env.getElementsAnnotatedWith(Service.class)
遍历元素,提取方法并将它们写入新文件,方法如下:

FileObject file = null;
file = filer.createSourceFile("com/test/" + serviceName);
file.openWriter().append(serviceContent).close();
--选项1-从命令行手动编译---

我试着去做你想做的,那就是从一个处理器访问所有的类,正如人们评论的那样,javac总是编译所有的类,而从RoundEnvironment我每次都可以访问正在编译的所有类(即使没有文件更改),有一个小细节:只要所有类都显示在要编译的类列表上

我已经用两个接口做了一些测试,其中一个(a)依赖于另一个(B)扩展,我有以下场景:

  • 如果我要求编译器只显式编译具有依赖项(A)的接口,将java文件的完整路径传递到命令行,并将输出文件夹添加到类路径,则只处理传递到命令行的接口
  • 如果我只显式编译(A),并且没有将输出文件夹添加到类路径,编译器仍然只处理接口(A)。但它也给了我一个警告:
    隐式编译的文件没有经过注释处理。
  • 如果我使用*或将这两个类传递给编译器到命令行中,那么我会得到预期的结果,两个接口都会得到处理
  • 如果您将编译器设置为verbose,您将得到一条明确的消息,显示在每一轮中将处理哪些类。这是我显式传递接口(A)时得到的结果:

    这就是我在添加这两个类时得到的结果:

    Round 1:
    input files: {com.bearprogrammer.test.AnotherInterface, com.bearprogrammer.test.TestInterface}
    annotations: [com.bearprogrammer.annotation.Service]
    last round: false
    
    在这两种情况下,我都看到编译器解析这两个类,但顺序不同。对于第一种情况(仅添加一个接口):

    对于第二种情况(添加了所有接口):

    这里的重要细节是,在第一种情况下,编译器将依赖项作为编译的隐式对象加载。在第二种情况下,它将加载它作为待编译对象的一部分(您可以看到这一点,因为它在解析提供的类之后开始搜索其他路径中的文件)。而且隐式对象似乎不包括在注释处理列表中

    有关编译过程的更多详细信息,请选中此项。这并没有明确说明要提取哪些文件进行处理

    这种情况下的解决方案是始终将所有类添加到编译器的命令中

    ---选项2-从Eclipse编译---

    如果您是从Eclipse编译,增量构建将使您的处理器失败(尚未测试)。但是我认为你可以要求一个干净的构建(Project>clean…,还没有测试过),或者编写一个Ant构建,总是清理类目录并设置一个

    ---选项3-使用构建工具---

    如果您正在使用其他构建工具,如Ant、Maven或Gradle,最好的解决方案是在编译之外的单独步骤中生成源代码。您还需要在单独的前一步中编译处理器(如果使用Maven/Gradle中构建的多项目,则需要单独的子项目)。这将是最佳方案,因为:

  • 对于处理步骤,您始终可以执行完全干净的“编译”,而无需实际编译代码(从javac使用选项
    -proc:only
    ,以仅处理文件)
  • 有了生成的源代码,如果您使用的是Gradle,那么如果生成的源文件没有更改,那么不重新编译它们就足够聪明了。Ant和Maven只会重新编译所需的文件(生成的文件及其依赖项)
  • 对于第三个选项,您还可以设置一个Ant构建脚本,从Eclipse生成这些文件,作为在Java构建器之前运行的构建器。在一些特殊文件夹中生成源文件,并将其添加到Eclipse中的classpath/buildpath中。

    NetBeans annotation为同一个包中的所有类生成单个Bundle.java文件。由于中的以下技巧,它可以正确地进行增量编译:

    Set-toProcess=new-HashSet();
    for(元素e:roundEnv.getElementsAnnotatedWith(Messages.class)){
    PackageElement包装=findPkg(e);
    对于(元素elem:pkg.getEnclosuringElements()){
    if(elem.getAnnotation(Message.class)!=null){
    toProcess.add(elem);
    }
    }
    }
    //现在在toProcess中处理所有包元素
    //而仅仅是那些由roundEnv提供的
    包装元件findPkg(元件e){
    对于(;;){
    if(PackageElement的实例){
    返回(PackageElement)e;
    }
    e=e.getEnclosuringElement();
    }
    }
    
    通过这样做,可以确保包中的所有(顶级)元素一起处理,即使编译仅在包中的单个源文件上调用


    如果您知道在何处查找注释(包中的顶级元素,甚至包中的任何元素),您应该能够始终获得所有此类元素的列表。

    您是否在Eclipse中运行此注释处理器?@johncarl的观点很重要,而且必须正确
    Round 1:
    input files: {com.bearprogrammer.test.TestInterface}
    annotations: [com.bearprogrammer.annotation.Service]
    last round: false
    
    Round 1:
    input files: {com.bearprogrammer.test.AnotherInterface, com.bearprogrammer.test.TestInterface}
    annotations: [com.bearprogrammer.annotation.Service]
    last round: false
    
    [parsing started RegularFileObject[src\main\java\com\bearprogrammer\test\TestInterface.java]]
    [parsing completed 15ms]
    [search path for source files: src\main\java]
    [search path for class files: ...]
    [loading ZipFileIndexFileObject[lib\processor.jar(com/bearprogrammer/annotation/Service.class)]]
    [loading RegularFileObject[src\main\java\com\bearprogrammer\test\AnotherInterface.java]]
    [parsing started RegularFileObject[src\main\java\com\bearprogrammer\test\AnotherInterface.java]]
    
    [parsing started RegularFileObject[src\main\java\com\bearprogrammer\test\AnotherInterface.java]]
    ...
    [parsing started RegularFileObject[src\main\java\com\bearprogrammer\test\TestInterface.java]]
    [search path for source files: src\main\java]
    [search path for class files: ...]
    ...
    
    Set<Element> toProcess = new HashSet<Element>();
    for (Element e : roundEnv.getElementsAnnotatedWith(Messages.class)) {
      PackageElement pkg = findPkg(e);
      for (Element elem : pkg.getEnclosingElements()) {
        if (elem.getAnnotation(Message.class) != null) {
          toProcess.add(elem);
        }
      }
    }
    // now process all package elements in toProcess 
    // rather just those provided by the roundEnv
    
    PackageElement findPkg(Element e) {
      for (;;) {
        if (e instanceof PackageElement) {
          return (PackageElement)e;
        }
        e = e.getEnclosingElement();
      }
    }