Java 8的很酷的新功能之一就是对lambda表达式的支持。 Lambda表达式在很大程度上依赖于FunctionalInterface
注释 。
在本文中,我们将介绍注释以及如何处理它们,以便您可以实现自己的出色功能。
注解
Java 5中添加了注释 。Java语言附带了一些预定义的注释 ,但是您也可以定义自定义注释 。
许多框架和库都充分利用了自定义注释。 例如, JAX-RS使用它们将POJO转换为REST资源。
注释可以在编译时或在运行时(或什至两者)进行处理。
在运行时,您可以使用反射API 。 可以注释的Java语言的每个元素(例如类或方法)都实现AnnotatedElement
接口。 请注意,只有具有RUNTIME
RetentionPolicy
的注释才可以在运行时RUNTIME
。
编译时注释处理
Java 5带有单独的apt
工具来处理注释,但是自Java 6起,此功能已集成到编译器中。
您可以直接从命令行调用编译器,例如从命令行调用,也可以从程序间接调用。
在前一种情况下,您可以为javac
指定-processor
选项 ,或者通过将文件META-INF/services/javax.annotation.processing.Processor
到jar META-INF/services/javax.annotation.processing.Processor
来使用ServiceLoader
框架。 该文件的内容应为一行,其中包含处理器类的完全限定名称。
ServiceLoader
方法在自动构建中特别方便,因为您要做的就是在编译过程中将注释处理器放在类路径上,而Maven或Gradle之类的构建工具将为您提供帮助。
从应用程序内部进行编译时注释处理
您还可以使用编译时工具从正在运行的应用程序中处理注释。
与其直接调用javac
,不如使用更方便的JavaCompiler
接口。 无论哪种方式,您都需要使用JDK而不是JRE运行应用程序。
JavaCompiler
接口使您能够以编程方式访问Java编译器。 您可以使用ToolProvider.getSystemJavaCompiler()
获得此接口的实现。 此方法对JAVA_HOME
环境变量敏感。
JavaCompiler
的getTask()
方法允许您添加注释处理器实例 。 这是控制注释处理器构造的唯一方法。 调用注释处理器的所有其他方法都要求处理器具有公共的无参数构造函数。
注释处理器
处理器必须实现Processor
接口。 通常,您将要扩展AbstractProcessor
基类,而不是从头开始实现接口。
每个注释处理器必须通过getSupportedAnnotationTypes()
方法指示其感兴趣的注释类型。 您可以返回*
以处理所有注释。
另一个重要的事情是指出您支持的Java语言版本。 重写getSupportedSourceVersion()
方法并返回RELEASE_x
常量之一。
通过实现这些方法,注释处理器就可以开始工作了。 处理器的作用在process()
方法中。
当process()
返回true
,处理的注释被这个处理器权利 ,并且将不被提供给其它处理器。 通常,您应该与其他处理器配合使用并返回false
。
元素和类型镜
注释和它们所在的Java元素作为Element
对象提供给您的process()
方法。 您可能需要使用Visitor模式处理它们 。
最有趣的元素类型是用于类和接口(包括注释)的TypeElement
,用于方法的ExecutableElement
和用于字段的VariableElement
。
每个Element
指向一个TypeMirror
,它表示Java编程语言中的一个类型。 您可以使用TypeMirror
来遍历正在处理的带注释的代码的类关系,就像在JVM中运行的代码上使用反射一样。
加工回合
注释处理发生在称为rounds的单独阶段中。 在每个回合中,处理器都有机会处理其感兴趣的注释。
可通过传递到process()
方法中的RoundEnvironment
参数获得要处理的注释及其上存在的元素。
如果注释处理器在一轮中生成新的源文件或类文件,则编译器将使这些文件可用于下一轮处理。 这将继续,直到不再生成新文件为止。
最后一轮不包含任何输入,因此是释放处理器可能已获取的任何资源的好机会。
初始化和配置处理器
注释处理器使用ProcessingEnvironment
初始化 。 此处理环境使您可以创建新的源文件或类文件 。
它还以选项的形式提供对配置的访问。 选项是键值对,您可以使用-A
option在命令行上提供给javac
。 为此,必须在处理器的getSupportedOptions()
方法中返回选项的键。
最后,处理环境提供了一些在处理过程中派上用场的支持例程(例如, 获取元素的JavaDoc或获取类型的直接超类型 )。
类路径问题
为了在注释处理过程中获得最准确的信息,您必须确保所有导入的类都在类路径上,因为引用不可用类型的类可能具有不完整的信息或完全缺少信息。
当处理大量带注释的类时,这可能会在Windows系统上导致命令行太大 (> 8K)的问题。 即使使用JavaCompiler
接口,它仍会在后台调用javac
。
Java编译器有一个很好的解决方案:您可以使用包含javac
参数的参数文件 。 然后,在命令行上提供参数文件的名称,并在@
之前。
不幸的是, JavaCompiler.getTask()
方法不支持参数文件,因此您必须使用基础的run()
方法。
请记住, getTask()
方法是唯一一种允许您构造注释处理器的方法。 如果必须使用参数文件,则必须使用公共的无参数构造函数。
如果遇到这种情况,并且有多个注释处理器需要共享一个类的单个实例,则无法将该实例传递到构造函数中,因此将被迫使用诸如Singleton模式之类的东西。
结论
注释是一项令人兴奋的技术,具有许多有趣的应用程序。 例如,我使用它们将REST API中的资源提取到资源模型中以进行进一步处理,例如生成文档。
翻译自: https://www.javacodegeeks.com/2015/01/how-to-process-java-annotations.html