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

Poi*_*idi 14 java annotation-processing

我有两个类的方法,我想将两个类的方法组合到一个类.

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

@Service("ITestService")
public interface ITest2
{
   @Export
   void method2();
}
Run Code Online (Sandbox Code Playgroud)

结果应该是:

public interface ITestService extends Remote
{
  void method1();
  void method2();
}
Run Code Online (Sandbox Code Playgroud)

我的AnnotationProcessor的第一次运行生成正确的输出(因为RoundEnvironment包含两个类).

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

public interface ITestService extends Remote
{
  void method1();
  void newMethod();
}
Run Code Online (Sandbox Code Playgroud)

现在缺少method2.我不知道如何解决我的问题.有没有办法(Enviroment),访问项目中的所有类?或者还有另一种解决方法吗?

生成类的代码很长,所以这里简要介绍我如何生成类.我遍历Elements env.getElementsAnnotatedWith(Service.class)并提取方法并将它们写入新文件:

FileObject file = null;
file = filer.createSourceFile("com/test/" + serviceName);
file.openWriter().append(serviceContent).close();
Run Code Online (Sandbox Code Playgroud)

vis*_*ola 8

- 选项1 - 从命令行手动编译---

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

我用两个接口完成了一些测试,其中一个(A)依赖于(B)其他(扩展),我有以下场景:

  1. 如果我要求编译器只显式编译具有依赖项(A)的接口,将完整路径传递给java文件到命令行,并将输出文件夹添加到类路径,只有我传入命令行的接口得到处理.
  2. 如果我只显式编译(A)并且不将输出文件夹添加到类路径中,则编译器仍然只处理接口(A).但它也给了我警告:Implicitly compiled files were not subject to annotation processing.
  3. 如果我使用*或将两个类传递给编译器到命令行,那么我得到预期的结果,两个接口都被处理.

如果将编译器设置为详细,那么您将获得明确的消息,显示每轮中将处理哪些类.这是我明确传递接口(A)时得到的:

Round 1:
input files: {com.bearprogrammer.test.TestInterface}
annotations: [com.bearprogrammer.annotation.Service]
last round: false
Run Code Online (Sandbox Code Playgroud)

这就是我添加两个类时所得到的:

Round 1:
input files: {com.bearprogrammer.test.AnotherInterface, com.bearprogrammer.test.TestInterface}
annotations: [com.bearprogrammer.annotation.Service]
last round: false
Run Code Online (Sandbox Code Playgroud)

在这两种情况下,我都看到编译器会解析这两个类,但顺序不同.对于第一种情况(仅添加一个接口):

[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]]
Run Code Online (Sandbox Code Playgroud)

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

[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: ...]
...
Run Code Online (Sandbox Code Playgroud)

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

有关编译过程的更多详细信息,请查看此编译概述.这没有明确说明要处理哪些文件.

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

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

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

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

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

  1. 对于处理步骤,您始终可以执行完全干净的"编译"而无需实际编译代码(使用-proc:onlyjavac中的选项仅处理文件)
  2. 使用生成的源代码,如果您使用的是Gradle,那么如果生成的源文件没有更改,则不会重新编译生成的源文件.Ant和Maven只会重新编译所需的文件(生成的文件及其依赖项).

对于第三个选项,您还可以设置Ant构建脚本,以便从Eclipse生成这些文件,作为在Java构建器之前运行的构建器.在一些特殊文件夹中生成源文件,并将其添加到Eclipse中的classpath/buildpath.