当前位置: 首页 > 面试题库 >

如何通过AnnotationProcessor访问TypeUse注释

濮彬
2023-03-14
问题内容
  1. 是否可以@Target(ElementType.TYPE_USE)通过注释处理器访问带有注释的元素?
  2. 是否可以通过注释处理器访问带注释的类型边界?

高度赞赏我错过的相关文档的链接。

内容:

注释:

@Target(ElementType.TYPE_USE)
@Retention(RetentionPolicy.SOURCE)
public @interface TypeUseAnno {}

一个示例类:

public class SomeClass extends HashMap<@TypeUseAnno String, String> {}

处理器:

@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes("base.annotations.TypeUseAnno")
public class Processor extends AbstractProcessor {

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        this.processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, "Initialized.");
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        this.processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, "Invoked.");
        for (TypeElement annotation : annotations) {
            this.processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, "" + roundEnv.getElementsAnnotatedWith(annotation));
        }
        return true;
    }
}

在classpath
SomeClass使用编译以上内容Processor将显示"Intialized"消息,但process(...)永远不会调用该方法。@Target(ElementType.PARAMETER)当方法参数中存在注释时,使用注释向处理器添加另一个注释可以正常工作。如果方法参数带有注释,@TypeUseAnno则过程将再次忽略该元素。


问题答案:

TYPE_USE注释是有点棘手,因为编译器将这些不同的,比“旧用法”的注释。

因此,正如您正确观察到的那样,它们不会传递给注释处理器,您的process()方法将永远不会收到它们。

那么如何在编译时使用它们呢?

在Java
8中,引入了这些注释,还引入了附加到Java编译的新方法。现在,您可以将侦听器附加到编译任务,并触发您自己对源代码的遍历。因此,访问批注的任务分为两部分。

  1. 钩到编译器
  2. 实施您的分析仪

广告1. Java
8中有2个选项可以挂接编译器。1.使用新的编译器插件API(https://docs.oracle.com/javase/8/docs/jdk/api/javac/tree/com/sun
/source/util/Plugin.html)2
.使用注释处理器

我没有太多使用选项#1,因为它需要明确地指定为javac参数。因此,我将介绍选项1:

您必须附加TaskListener到正确的编译阶段。有多个阶段。紧随其后的是唯一的一个,在此期间您具有可访问的语法树,该语法树表示包括方法主体在内的完整源代码(请记住,TYPE_USE即使在局部变量声明中也可以使用注释。

@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class EndProcessor extends AbstractProcessor {

    @Override
    public synchronized void init(ProcessingEnvironment env) {
        super.init(env);
        Trees trees = Trees.instance(env);
        JavacTask.instance(env).addTaskListener(new TaskListener() {

            @Override
            public void started(TaskEvent taskEvent) {
                // Nothing to do on task started event.
            }

            @Override
            public void finished(TaskEvent taskEvent) {
                if(taskEvent.getKind() == ANALYZE) {
                    new MyTreeScanner(trees).scan(taskEvent.getCompilationUnit(), null);
                }
            }

        });
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        // We don't care about this method, as it will never be invoked for our annotation.
        return false;
    }
}

广告2。现在,他们MyTreeScanner可以扫描完整的源代码,并找到注释。无论您是否使用Pluginor
AnnotationProcessor方法,都适用。这仍然很棘手。您必须实现TreeScanner或通常扩展TreePathScanner。这代表一种访客模式,您必须在其中进行适当的分析,以了解哪些元素是您感兴趣的。

让我们举一个简单的例子,它可以以某种方式对局部变量声明做出反应(给我5分钟):

class MyTreeScanner extends TreePathScanner<Void, Void> {
    private final Trees trees;

    public MyTreeScanner(Trees trees) {
        this.trees = trees;
    }

    @Override
    public Void visitVariable(VariableTree tree, Void aVoid) {
        super.visitVariable(variableTree, aVoid);
        // This method might be invoked in case of
        //  1. method field definition
        //  2. method parameter
        //  3. local variable declaration
        // Therefore you have to filter out somehow what you don't need.
        if(tree.getKind() == Tree.Kind.VARIABLE) {
            Element variable = trees.getElement(trees.getPath(getCurrentPath().getCompilationUnit(), tree));
            MyUseAnnotation annotation = variable.getAnnotation(MyUseAnnotation.class);
            // Here you have your annotation.
            // You can process it now.
        }
        return aVoid;
    }
}

这是非常简短的介绍。对于真实的示例,您可以查看以下项目源代码:https :
//github.com/c0stra/fluent-api-end-
check/tree/master/src/main/java/fluent/api/processors

在开发此类功能时进行良好的测试也非常重要,因此您可以调试,反向工程并解决您将在该领域遇到的所有棘手问题;)为此,您还可以从这里获得启发:
https://github.com/github.com。 com / c0stra / fluent-api-end-check / blob /
master / src / test / java / fluent / api /
EndProcessorTest.java

也许是我的最后一句话,因为javac确实对批注的使用有所不同,所以存在一些限制。例如,它不适合触发Java代码生成,因为编译器不会选择在此阶段创建的文件进行进一步编译。



 类似资料:
  • 我看了一些其他帖子,试图复制他们所做的,但他们似乎都没有遇到与我相同的问题。基本上,我试图存储S3存储桶中的密钥列表,以便在我的Vue应用程序中迭代它们。我有下面的代码,你可以看到我有3个控制台。记录我试图打印文件值的语句。第一个完全按照我的预期打印,而第二个打印,而第三个根本不打印。因此,出于某种原因,它没有将文件的值持久化到s3之外。listObjectsV2()函数,这意味着我无法访问应用程

  • 问题内容: 这是一个测试类: 这是我的输出: 我缺少通过反射使注释可见的什么? 我是否仅需要检查它们的存在就需要注释处理器? 问题答案: 为了在运行时访问注释,它需要具有运行时的保留策略。 否则,注释将被丢弃,并且JVM无法识别它们。 有关更多信息,请参见此处。

  • 问题内容: 我开始按照Android所没有使用的教程进行操作,并得到以下信息: 我想这可以归结为几个问题: 我没有创建自己的信任库,但是在网上搜索教程和其他内容时,不确定如何创建信任库。有没有一种方法可以创建或修改信任库以在其中存储所需的证书?(如果有任何不同,我使用的是自签名证书) 如何使SSL握手顺利进行?现在,我得到的错误是: 老实说,我真的不明白这意味着什么。 我需要在Android设备上

  • 问题内容: 我试图弄清楚如何使用JavaFx。我在Scene Builder中构建了应用程序界面。但是我无法访问该组件,因为所有组件都已加载到Parent中。 如果我在“ Pane”上更改“ Parent”,则可以访问getChildren(),但是如果我知道fx:id …,还不清楚如何获得控制权。 问题更简单。我在Scene Builder中添加了Label或TextField。如果我知道fx:

  • 我正在尝试使用三个虚拟机(Master–10.x.x.4、Node1–10.x.x.150、Node2–10.x.x.160)创建Kubernetes集群。 我能够通过此链接成功创建留言簿应用程序:http://kubernetes.io/v1.0/examples/guestbook/.我只对frontend-service.yaml做了一个更改:使用NodePort。我可以使用节点IP和端口号