以编程方式调用注解处理器

7
这是我第一次编写注释处理器,我想以编程方式调用它。这是否可能?
我已经为处理器编写了小代码:
@SupportedAnnotationTypes({"app.dev.ems.support.annotation.HBMModel"})
public class HBMModelProcessor extends AbstractProcessor {

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(HBMModel.class);
        System.out.println(elements);
        return true;
    }

}

现在,如果我想调用process方法,我应该怎么做?我可以按以下方式执行吗:

HBMModelProcessor modelProcessor = new HBMModelProcessor();
modelProcessor.process(annotations, roundEnv)

任何信息都对我有很大帮助。
谢谢。
3个回答

4

您可以像这样在同一进程内以编程方式使用注释处理器调用Java编译器:

import com.sun.tools.javac.processing.PrintingProcessor;
import fi.jumi.actors.generator.JavaSourceFromString;
import org.junit.*;
import org.junit.rules.TemporaryFolder;

import javax.annotation.processing.Processor;
import javax.tools.*;
import javax.tools.JavaCompiler.CompilationTask;
import java.io.IOException;
import java.util.Arrays;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;

public class ExampleTest {

    @Rule
    public final TemporaryFolder tempDir = new TemporaryFolder();

    @Test
    public void example() throws IOException {
        JavaFileObject src = new JavaSourceFromString(
                "com.example.GuineaPig",
                "package com.example;\n" +
                "public interface GuineaPig {\n" +
                "    void foo();\n" +
                "}"
        );
        compile(new PrintingProcessor(), src);
    }

    private void compile(Processor processor, JavaFileObject... compilationUnits) throws IOException {
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
        StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null);
        fileManager.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(tempDir.getRoot()));

        CompilationTask task = compiler.getTask(null, fileManager, diagnostics, null, null, Arrays.asList(compilationUnits));
        task.setProcessors(Arrays.asList(
                processor
        ));
        boolean success = task.call();
        for (Diagnostic<? extends JavaFileObject> diagnostic : diagnostics.getDiagnostics()) {
            System.err.println(diagnostic);
        }
        assertThat("compile succeeded", success, is(true));
    }
}

如果您删除对setProcessors的调用,则它将根据类路径上的META-INF/services/javax.annotation.processing.Processor文件自动检测注解处理器。

4

jOOR具有API来简化访问javax.tools.JavaCompiler,如此答案所示。您可以按以下方式轻松触发它:

Reflect.compile(
    "com.example.MyClass",
    "package com.example; "
  + "@app.dev.ems.support.annotation.HBMModel "
  + "class MyClass {}",
    new CompileOptions().processors(new HBMModelProcessor())
);

这对于单元测试注解处理器特别有用。此外,请参见此处的博客文章: https://blog.jooq.org/2018/12/07/how-to-unit-test-your-annotation-processor-using-joor

免责声明,我为jOOR背后的公司工作。


3
这是链接到我的类似问题的答案
您可以按照您在问题中建议的方式进行注释处理,但是您必须以某种方式生成annotationsroundEnv
注释处理的预期使用是在编译期间。 我建议使用两步编译过程。
  1. 以通常的方式编译您的注释处理器和相关文件。
  2. 编译其他文件(使用支持注释处理的编译器)。 您可能需要向编译器提供一些参数:处理器路径、处理器的类名等。
编译器将生成annotationsroundEnv变量以及您的处理器的实例。(大多数编译器要求您的处理器是公共的并具有公共构造函数。)然后编译器将调用process方法。

谢谢回复。所以没有办法获取这两个变量 annotationsroundEnv 吗? - Tapas Bose
当然有一种方法可以获取annotationsroundEnvannotations = new HashSet<TypeElement>(); annotations.add( /* your annotations */);roundEnv = new RoundEnvironment() { /* implementation */ }。你正在尝试对处理器进行单元测试吗?你可能可以使用一个模拟框架。 - emory
@emory,我想对一个注解处理器进行单元测试。如果有示例将不胜感激,因为这是我在谷歌上的首次搜索结果。 - Snicolas
@Snicolas 我创建了一个 ideone,应该可以帮助你入门 - http://ideone.com/7HhgzI - emory
1
我终于成功了。我使用这个作为灵感来源:https://today.java.net/pub/a/today/2008/04/10/source-code-analysis-using-java-6-compiler-apis.html#invoking-the-compiler-from-code-the-java-compiler-api。我得到了一个处理器单元测试的示例[在这里](https://github.com/stephanenicolas/boundbox)。该库是使用这种技术进行测试的。 - Snicolas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接