如何在注解处理中引用方法的实现?

21
我正在研究Java(javax)注解处理。
假设我有一个用于方法的注解:
@Target(ElementType.METHOD)
public @interface MethodAnnotation { }

现在,我想处理所有从带注释方法的类型中重写的方法:
interface MyInterface() {
    @MethodAnnotation
    void f()
}

class MyClass implements MyInterface {
    override void f() { } // <- I want to process this method
}

@Inherited 元注释在这里似乎不太合适:

请注意,如果被注释的类型用于注释除类以外的任何内容,则此元注释类型无效。

另外,是否可能处理未在子类中被覆盖的继承类方法?像这样:

class MyClass {
    @MethodAnnotation
    void f() { }
}

class MySubClass extends MyClass { } // <- I want to process its f()
                                     //    or at least to find out that it doesn't
                                     //    override the method

我怎样才能访问AbstractProcessor中某个方法的被覆盖方法?
我猜需要找到封闭类的子类,但是我还没有找到一个方法来实现这一点。
更新:我认为可以使用RoundEnvironment.getRootElements()来实现,但仍未找到正确的方法。

你能否不迭代你的类的所有层次结构(超类和接口),并查找每个接口和超类哪些方法具有注释? - davidxxx
@davidhxxx,我猜注解处理走的是另一条路线:它在带有注解的代码元素处触发,不是吗?如果是这样的话,我需要找到没有注解覆盖的方法,给出具有此注解声明的方法。 - hotkey
抱歉,您提到了javax.annotation.processing.AbstractProcessor。 我以为您想对注解进行处理 :) - davidxxx
4个回答

11
简短的回答是,开箱即用的注解处理机制不会让这件事情变得容易,但是它是可以做到的。
与其使用正常的调度机制进行处理,实际上你需要处理每一个方法并自己进行过滤。
步骤1:
定义你的处理器,使其通过使用"*"作为支持的注解类型支持所有注解。这意味着你的处理器将在每一轮中被调用。
步骤2:
使用getRootElements获取每一轮的整个元素集合。
步骤3:
创建一个ElementScanner8来遍历任何元素以查找ExecutableElements。如果你愿意相信重写的方法被注释为@Override,你可以快速过滤这些方法。否则,就查看所有方法。
现在你需要查看该方法是否覆盖了具有所需注释的方法。没有简单的方法可以获取给定方法已覆盖的方法,因此您需要获取该方法的封闭元素,查看其超类实现的接口(递归),获取它们的封闭元素, 过滤方法, 并测试是否已被所讨论的方法覆盖。如果是,您可以检查注释以查看它是否包含您关心的注释。

步骤 5:

此时,您应该已经拥有了您正在寻找的覆盖方法、被覆盖方法和注释镜像,因此您应该能够实现您想要的任何逻辑。


2
根据Jsr269-1.8中javax.annotation.processing.Processor的JavaDoc文档,如果注释符合AnnotatedConstruct中的存在定义,则存在该注释。简而言之,如果直接存在或通过继承存在,则为了发现目的,将考虑存在注释。由于被容器注释包装,注释不被视为存在... AnnotatedConstruct#getAnnotationsByType 的JavaDoc说明它返回间接存在的注释,因此我认为您应该扫描方法并检查是否使用此调用间接具有注释。可以参考这里的代码实现。
免责声明... 我没试过 ;)

2

方法注解不会被继承。通过使用“@Inherited”注解,类型注解可以被继承。

你可以定义一个带有继承类型注解的函数式接口,但我不知道这是否足够优雅。


1
如果那些注释在运行时可用,并且您想在运行时访问它们,您可以使用Reflections库。
例如:
Collection<URL> urls = ClasspathHelper.forPackage("nl.shopname.location.domain");

Reflections reflections = new Reflections(
    new ConfigurationBuilder().setUrls(urls).setScanners(new FieldAnnotationsScanner()));

Set<Field> fieldsWithAnnotation = reflections.getFieldsAnnotatedWith(MyAnnotation.class); 

1
谢谢您的回答,但不幸的是我的目标是进行一些编译时代码验证,因此我需要编译时处理。 - hotkey

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