不使用注解处理Java类型

7
我有几个使用Java注解处理器执行的检查,但我还想对未注释的类型进行检查。
例如,假设我有一个注解@Responsible(name="xyz") ,如何最好地接入编译过程以强制要求所有顶层类型都包含注释?
在我的当前实现中,我依赖于两个注解,期望的注解(Responsible)和一个包级别的注解。即使没有期望的注解存在,后者也用于“启动”注解处理器。然后,在触发的注解处理器内部,我搜索并过滤磁盘上的Java文件(使用传递给编译器的参数),以收集所有要处理的文件,并在处理器正在处理的带注释的类型对应的Java文件时对它们进行过滤。这样做的话,如果有人提交了一个没有指定注解的新文件,则构建将失败。
有没有一种更简洁的方法来查找“非注释”的类型呢?
2个回答

5
您不必依赖注释来运行处理器。如文档所述:

如果不存在注释类型,则仍会进行注释处理,但只有支持处理“*”的通用处理器才能声明(空)注释类型集。

您寻找类的方法有些笨拙。相反,您可以依靠包和类之间的父子关系:找出包含有趣类的顶级包元素的名称,并使用Element#getEnclosedElements进入该包(及/或其子包)。或者,您可以在该包中定位单个类,然后使用Element#getEnclosingElement上升到最顶层的包。包对象可以通过Elements#getPackageElement按名称获取,类对象可以通过Elements#getTypeElement按名称获取。
与手动操作文件和目录相比,这种方法要少得多,而且如果源文件移动或在目录之间分割,它也不会出错。
请注意,包含顺序为"单个包" -> "类" -> "方法" -> ...,每个包的父级是一个包本身,而不是它的“父”包(net.example不包含在net中)。

问题在于Element#getEnclosedElements无法遍历子包,我将使用“*”处理器和roundEnv.getRootElements()进行更深入的调查。 - Matthieu BROUILLARD
@MatthieuBROUILLARD 是的,这正是我上面所说的。幸运的是,没有任何阻止你使用String#split自行获取子包 :) - user1643723
感谢您在使用“*”方面的提示,这是正确的方法。 - Matthieu BROUILLARD

4
为了处理所有元素,我错过了使用*来声明处理器要处理所有元素时,RoundEnvironment#getRootElements()就是我要查找的元素列表。
因此,为了检查所有类型是否都带有@Responsible注解,我最终得到了以下代码。
@AutoService(Processor.class)
public class ResponsibleAnnotationProcessor extends AbstractProcessor {
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
            List<ElementKind> typeKinds = Arrays.asList(ElementKind.ENUM, ElementKind.INTERFACE, ElementKind.CLASS);
            // let's gather all types we are interrested in
            Set<String> allElements = env.getRootElements()
                    .stream()
                    .filter(e -> typeKinds.contains(e.getKind()))   // keep only interesting elements
                    .map(e -> e.asType().toString())                // get their full name
                    .collect(Collectors.toCollection(() -> new HashSet<>()));
            Set<String> typesWithResponsible = new HashSet<>();

            annotations.forEach(te -> {
                if (Responsible.class.getName().equals(te.asType().toString())) {
                    // We collect elements with an already declared  ownership 
                    env.getElementsAnnotatedWith(te).forEach(e -> typesWithResponsible.add(e.asType().toString()));
                }
            });

            allElements.removeAll(typesWithResponsible);
            allElements.forEach(cname -> processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, cname + " must be annotated with @" + Responsible.class.getName() + " to declare a ownership"));
            return false;
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
      return SourceVersion.latestSupported();
    }

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        // We want to process all elements
        return Collections.singleton("*");
    }
}

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