该
TYPE_USE注释是有点棘手,因为编译器将这些不同的,比“旧用法”的注释。
因此,正如您正确观察到的那样,它们不会传递给注释处理器,您的
process()方法将永远不会收到它们。
那么如何在编译时使用它们呢?
在Java
8中,引入了这些注释,还引入了附加到Java编译的新方法。现在,您可以将侦听器附加到编译任务,并触发您自己对源代码的遍历。因此,访问批注的任务分为两部分。
- 钩到编译器
- 实施您的分析仪
广告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代码生成,因为编译器不会选择在此阶段创建的文件进行进一步编译。



