java处理注释_Java注释处理器

java处理注释

本文是我们名为“ 高级Java ”的学院课程的一部分。

本课程旨在帮助您最有效地使用Java。 它讨论了高级主题,包括对象创建,并发,序列化,反射等。 它将指导您完成Java掌握的旅程! 在这里查看 !

目录

1.简介 2.何时使用注释处理器 3.后台处理注释 4.编写自己的注释处理器 5.运行注释处理器 6.接下来 7.下载源代码

1.简介

在本部分的教程中,我们将揭开注释处理的魔力,它通常用于检查,修改或生成仅由注释驱动的源代码。 本质上,注释处理器是Java编译器的某种插件。 明智地使用注释处理器可以大大简化Java开发人员的工作,因此这就是为什么它们通常与许多流行的库和框架捆绑在一起的原因。

作为编译器插件还意味着注释处理器有点底层,并且高度依赖Java版本。 但是,本教程的第5部分中的有关注释的知识以及本教程的第13 部分中的如何以及何时使用Enums和Annotations和Java编译器API ,对理解Java编译器的内在细节将非常有用。注释处理器如何工作。

2.何时使用注释处理器

正如我们简要提到的那样,批注处理器通常用于检查代码库是否存在特定的批注,并根据用例来:

  • 生成一组源文件或资源文件
  • 更改(修改)现有源代码
  • 分析现有的源代码并生成诊断消息

注释处理器的有用性很难高估。 它们可以显着减少开发人员必须编写的代码量(通过生成或修改一个),或者通过进行静态分析来提示开发人员是否不满足特定注释所表示的假设。

对于开发人员来说,注解处理器几乎是不可见的,它被所有现代Java IDE和流行的构建工具完全支持,并且通常不需要任何特定的入侵。 在本教程的下一部分中,我们将构建自己的有些天真的注释处理器,尽管如此,它们仍将展示此Java编译器功能的全部功能。

3.后台处理注释

在深入研究自己的注释处理器的实现之前,最好先了解一下它的机制。 批注处理按一系列回合进行。 在每一轮中,可能会要求注释处理器处理在上一轮产生的源文件和类文件中找到的注释子集。

请注意,如果要求注释处理器在给定回合中进行处理,则即使没有注释要处理,也将要求其在后续回合中进行处理,包括最后一轮。

本质上,任何Java类都可以通过实现单个接口javax.annotation.processing.Processor成为全功能注释处理器。 但是,要真正变得可用, javax.annotation.processing.Processor每个实现都必须提供一个公共的无参数构造函数(有关更多详细信息,请参阅教程的第1部分如何创建和销毁对象 ),该方法可以用于实例化处理器。 处理基础结构将遵循一组规则以与注释处理器进行交互,并且处理器必须遵守以下协议:

  • 使用处理器类的无参数构造函数创建注释处理器的实例
  • 通过适当的javax.annotation.processing.ProcessingEnvironment实例调用init方法
  • 正在调用getSupportedAnnotationTypesgetSupportedOptionsgetSupportedSourceVersion方法(这些方法每次运行仅调用一次,而不是在每个回合中调用一次)
  • 最后,在适当时调用javax.annotation.processing.Processor上的处理方法(请考虑到不会为每个回合创建新的注释处理器实例)

Java文档强调,如果在未遵循上述协议的情况下创建和使用注释处理器实例,则该接口规范不会定义处理器的行为。

4.编写自己的注释处理器

我们将从最简单的一种不变性检查器开始,开发几种注释处理器。 让我们定义一个简单的注释Immutable ,我们将使用它来注释该类,以确保它不允许修改其状态。

@Target( ElementType.TYPE )
@Retention( RetentionPolicy.CLASS )
public @interface Immutable {
}

遵循保留策略,注释将在编译阶段由Java编译器保留在类文件中,但在运行时将不可用(也不应使用)。

从本教程的第3部分“ 如何设计类和接口”中我们已经知道,不可变性在Java中确实很难。 为简单起见,我们的注释处理器将验证该类的所有字段都声明为final。 幸运的是,Java标准库提供了一个抽象注释处理器javax.annotation.processing.AbstractProcessor ,它被设计为大多数具体注释处理器的便捷超类。 让我们看一下SimpleAnnotationProcessor注释处理器的实现。

@SupportedAnnotationTypes( "com.javacodegeeks.advanced.processor.Immutable" )
@SupportedSourceVersion( SourceVersion.RELEASE_7 )
public class SimpleAnnotationProcessor extends AbstractProcessor {@Overridepublic boolean process(final Set< ? extends TypeElement > annotations, final RoundEnvironment roundEnv) {for( final Element element: roundEnv.getElementsAnnotatedWith( Immutable.class ) ) {if( element instanceof TypeElement ) {final TypeElement typeElement = ( TypeElement )element;for( final Element eclosedElement: typeElement.getEnclosedElements() ) {if( eclosedElement instanceof VariableElement ) {final VariableElement variableElement = ( VariableElement )eclosedElement;if( !variableElement.getModifiers().contains( Modifier.FINAL ) ) {processingEnv.getMessager().printMessage( Diagnostic.Kind.ERROR,String.format( "Class '%s' is annotated as @Immutable, but field '%s' is not declared as final", typeElement.getSimpleName(), variableElement.getSimpleName()            ) );						}}}}// Claiming that annotations have been processed by this processor return true;}
}

SupportedAnnotationTypes注释可能是最重要的细节,它定义了此注释处理器感兴趣的注释类型。可以在此处使用*来处理所有可用的注释。

由于提供了脚手架,因此我们的SimpleAnnotationProcessor只需要实现一个方法process 。 实现本身非常简单,基本上只验证要处理的类是否声明了没有final修饰符的任何字段。 让我们看一下违反该天真不变性契约的类的示例。

@Immutable
public class MutableClass {private String name;public MutableClass( final String name ) {this.name = name;}public String getName() {return name;}
}

针对此类运行SimpleAnnotationProcessor将在控制台上输出以下错误:

Class 'MutableClass' is annotated as @Immutable, but field 'name' is not declared as final

因此,确认注释处理器成功检测到可变类上Immutable注释的滥用。

总的来说,执行自省(和代码生成)是大部分时间使用注释处理器的领域。 让我们复杂的任务一点点,从本教程中,该部13应用的Java编译器API的一些知识的Java编译器API 。 我们这次要编写的注释处理器将通过将final修饰符直接添加到类字段声明中来更改(或修改)生成的字节码,以确保不会在其他任何地方重新分配该字段。

@SupportedAnnotationTypes( "com.javacodegeeks.advanced.processor.Immutable" )
@SupportedSourceVersion( SourceVersion.RELEASE_7 )
public class MutatingAnnotationProcessor extends AbstractProcessor {private Trees trees; @Overridepublic void init (ProcessingEnvironment processingEnv) {super.init( processingEnv );trees = Trees.instance( processingEnv );        }@Overridepublic boolean process( final Set< ? extends TypeElement > annotations, final RoundEnvironment roundEnv) {final TreePathScanner< Object, CompilationUnitTree > scanner = new TreePathScanner< Object, CompilationUnitTree >() {@Overridepublic Trees visitClass(final ClassTree classTree, final CompilationUnitTree unitTree) {if (unitTree instanceof JCCompilationUnit) {final JCCompilationUnit compilationUnit = ( JCCompilationUnit )unitTree;// Only process on files which have been compiled from sourceif (compilationUnit.sourcefile.getKind() == JavaFileObject.Kind.SOURCE) {compilationUnit.accept(new TreeTranslator() {public void visitVarDef( final JCVariableDecl tree ) {super.visitVarDef( tree );if ( ( tree.mods.flags & Flags.FINAL ) == 0 ) {tree.mods.flags |= Flags.FINAL;}}});}}return trees;}};for( final Element element: roundEnv.getElementsAnnotatedWith( Immutable.class ) ) {    final TreePath path = trees.getPath( element );scanner.scan( path, path.getCompilationUnit() );} // Claiming that annotations have been processed by this processor return true;}
}

实现变得更加复杂,但是许多类(例如TreePathScannerTreePath )应该已经很熟悉了。 对同一个MutableClass类运行注释处理器将生成以下字节码(可以通过执行javap -p MutableClass.class命令来验证):

public class com.javacodegeeks.advanced.processor.examples.MutableClass {private final java.lang.String name;public com.javacodegeeks.advanced.processor.examples.MutableClass(java.lang.String);public java.lang.String getName();
}

实际上, name字段具有final修饰符,但在原始Java源文件中已将其省略。 我们的最后一个示例将展示注释处理器的代码生成功能(并结束讨论)。 同样,让我们​​实现一个注释处理器,该处理器将通过将Immutable后缀附加到使用Immutable注释进行注释的类名来生成新的源文件(分别是新类)。

@SupportedAnnotationTypes( "com.javacodegeeks.advanced.processor.Immutable" )
@SupportedSourceVersion( SourceVersion.RELEASE_7 )
public class GeneratingAnnotationProcessor extends AbstractProcessor {@Overridepublic boolean process(final Set< ? extends TypeElement > annotations, final RoundEnvironment roundEnv) {for( final Element element: roundEnv.getElementsAnnotatedWith( Immutable.class ) ) {if( element instanceof TypeElement ) {final TypeElement typeElement = ( TypeElement )element;final PackageElement packageElement = ( PackageElement )typeElement.getEnclosingElement();try {final String className = typeElement.getSimpleName() + "Immutable";final JavaFileObject fileObject = processingEnv.getFiler().createSourceFile(packageElement.getQualifiedName() + "." + className);try( Writer writter = fileObject.openWriter() ) {writter.append( "package " + packageElement.getQualifiedName() + ";" );writter.append( "\\n\\n");writter.append( "public class " + className + " {" );writter.append( "\\n");writter.append( "}");}} catch( final IOException ex ) {processingEnv.getMessager().printMessage(Kind.ERROR, ex.getMessage());}}}// Claiming that annotations have been processed by this processor return true;}
}

作为将此注释处理器注入MutableClass类的编译过程的结果,将生成以下文件:

package com.javacodegeeks.advanced.processor.examples;public class MutableClassImmutable {
}

不过,源文件及其类是使用原始字符串连接生成的(事实上,该类确实非常无用),目的是演示注释处理器执行的代码生成是如何工作的,因此可以应用更复杂的生成技术。

5.运行注释处理器

Java编译器通过支持–processor命令行参数,可以轻松地将任意数量的注释处理器插入到编译过程中。 例如,这是通过在MutableClass.java源文件的编译期间将其作为javac工具的参数传递而运行MutatingAnnotationProcessor的一种方法:

javac -cp processors/target/advanced-java-part-14-java7.processors-0.0.1-SNAPSHOT.jar -processor com.javacodegeeks.advanced.processor.MutatingAnnotationProcessor    -d examples/target/classesexamples/src/main/java/com/javacodegeeks/advanced/processor/examples/MutableClass.java

仅编译一个文件看起来并不复杂,但是现实生活中的项目包含成千上万个Java源文件,而从命令行使用javac工具编译这些文件实在是太过分了。 社区很可能已经开发了很多很棒的构建工具(例如Apache Maven , Gradle , sbt , Apache Ant等等),这些工具负责调用Java编译器并做很多其他事情,因此,如今大多数Java项目在那里至少使用其中之一。 例如,以下是从Apache Maven构建文件( pom.xml )调用MutatingAnnotationProcessor的方法:

<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.1</version><configuration><source>1.7</source><target>1.7</target><annotationProcessors>
<proc>com.javacodegeeks.advanced.processor.MutatingAnnotationProcessor</proc></annotationProcessors></configuration>
</plugin>

6.接下来

在本教程的这一部分中,我们对注解处理器及其帮助检查源代码,变异(修改)结果字节码或生成新的Java源文件或资源的方式进行了深入研究。 批注处理器通常用于使Java开发人员从遍布整个代码库的批注中派生出来,从而免于编写大量样板代码。 在本教程的下一部分中,我们将介绍Java代理以及操作JVM在运行时解释字节码的方式。

7.下载源代码

您可以在此处下载本课程的源代码: advanced-java-part-14

翻译自: https://www.javacodegeeks.com/2015/09/java-annotation-processors.html

java处理注释

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/337390.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++ vector类的模拟实现

点击蓝字关注我们1.前言vector和string虽然底层都是通过顺序表来实现的&#xff0c;但是他们利用顺序表的方式不同&#xff0c;string是指定好了类型&#xff0c;通过使用顺序表来存储并对数据进行操作&#xff0c;而vector是利用了C中的泛型模板&#xff0c;可以存储任何类型的…

visual studio源文件的编译顺序是依据什么?

问题&#xff1a;visual studio源文件的编译顺序是依据什么&#xff1f; 结论&#xff1a;依据 .vcxproj 文件里 指定了ClCompile的ItemGroup &#xff0c;如下图所示&#xff0c;就是这么简单粗暴。

并发运行的最佳实践_并发最佳实践

并发运行的最佳实践本文是我们名为“ 高级Java ”的学院课程的一部分。 本课程旨在帮助您最有效地使用Java。 它讨论了高级主题&#xff0c;包括对象创建&#xff0c;并发&#xff0c;序列化&#xff0c;反射等。 它将指导您完成Java掌握的旅程&#xff01; 在这里查看 &#…

功能齐全的屏幕截图C++实现详解

点击蓝字关注我们1、概述要使用屏幕截图&#xff0c;其实很容易&#xff0c;装一款聊天软件或者办公软件就可以了&#xff0c;比如QQ、企业微信、钉钉、飞书等。但要开发出类似这些软件的屏幕截图模块&#xff0c;则没那么容易。其实实现屏幕截图的技术并不复杂&#xff0c;主要…

如何判断exe文件是debug还是release编译生成的

如何判断exe文件是debug还是release编译生成的结论&#xff1a; 用IDA工具打开exe&#xff0c;然后看Imports里面的依赖库是否有带d或D结尾的&#xff0c;如果有就说明是Debug的 实验&#xff1a;&#xff08;实验环境 vs2017&#xff0c; IDA工具&#xff09; &#xff08;0&…

大屏可视化分配率是什么意思_什么是分配率?

大屏可视化分配率是什么意思诸如“不可持续的分配率”和“您需要保持较低的分配率”之类的短语似乎仅属于Java Champions的词汇表。 复杂&#xff0c;恐怖并被魔术光环包围。 经常发生的情况是&#xff0c;当您更仔细地查看概念时&#xff0c;魔术会随着抽烟消失。 这篇文章试…

C/C++语言动态开辟的杨辉三角

点击蓝字关注我们问题引入杨辉三角相必大家并不陌生&#xff0c;第1行有1列、第二行有2列…第n行有n列&#xff0c;且每行行首和行尾的值都为1&#xff0c;其余的值为上一行两数相加我们在C语言阶段&#xff0c;第一次碰到的杨辉三角应该都是用常规的二维数组存储&#xff0c;可…

git gui 历史版本_这些Git命令都不会,还是不要去面试了

前言以下&#xff0c;项目中经常使用的Git命令&#xff0c;汇总到这里以便与你能快速的学习和掌握Git命令&#xff0c;在文章最后有惊喜哟&#xff0c;一定要看到最后啊&#xff01;使用的 Git版本&#xff1a;git version 2.24.0命令git log# 输出概要日志,这条命令等同于# gi…

java restful_Java EE中的RESTful计时器

java restful在这篇文章中...。 EJB计时器旋风之旅 通过带有示例实现的简单REST接口即时使用EJB计时器 更新&#xff08;2015年7月14日&#xff09; 该应用程序的前端现在可以在OpenShift上使用 。 由于我是前端新手&#xff0c;因此我在其他来源的帮助下组装了此HTML5 Ang…

【lua学习】1.源码组织

虚拟机核心相关文件列表内嵌库相关文件解释器&#xff0c;字节码编译器相关的文件做cocos2d-x lua已经有一段时间了&#xff0c;想更深入了解lua。我会出一系列地 自身学习过程中地解读。我会带大家沿着源码来逐步解读lua&#xff0c;我喜欢按照 深度优先遍历的顺序来解读源码&…

c# 联合halcon 基于相关性 模板匹配_机器视觉之halcon入门(5)-字符识别exe生成...

2.3.2 第二个halcon程序转EXE程序&#xff1a;字符识别老规矩&#xff0c;每一段halcon代码得用C#二次开发下。根据上一节所教的&#xff0c;我们配置下C#的环境&#xff0c;顺便添加好控件&#xff0c;如下图(2-3-2-1)。图 2-3-2-1控件基本跟上一节一样&#xff0c;只是少了一…

C语言数据的存储和取出(超详细讲解)

点击蓝字关注我们整形的储存我们知道一个整形的存储是以补码的形式储存取出是原码的形式。比如&#xff1a;int a 5;的二进制是101那它的原码应该是&#xff1a;00000000 00000000 00000000 00000101正数的原反补相同那它存进去和取出来都是&#xff1a;00000000 00000000 000…

go语言 不支持动态加载_动态语言支持

go语言 不支持动态加载本文是我们名为“ 高级Java ”的学院课程的一部分。 本课程旨在帮助您最有效地使用Java。 它讨论了高级主题&#xff0c;包括对象创建&#xff0c;并发&#xff0c;序列化&#xff0c;反射等。 它将指导您完成Java掌握的旅程&#xff01; 在这里查看 &am…

【lua学习】2.数据类型

【lua学习】2.数据类型Lua中的数据类型关于TValue自顶向下分析TValue表示所有的Lua数据结构并带一个类型字段Value表示所有的Lua数据结构GCObject表示所有需要进行垃圾回收的数据结构GCheader表示需要GC的数据结构最开始的部分Lua中的数据类型 宏名 (见lua.h)宏值类型对应数据…

打印pdf就一页_PDF 文件转换工具

是将 PDF 文件转换为完全可编辑的 Windows 文档最好的转换软件。无论您需要您的内容是 Microsoft Word、Excel、PowerPoint、HTML 还是仅需要文本&#xff0c; 总会给您一个简单的方法&#xff0c;快捷地获取您要的内容。可转换整个文档或选择内容。亦可创建 PDF 文件。PDF 转换…

C++类的this指针,静态成员,友元函数友元类

点击蓝字关注我们1. this指针在上篇讲C中类&#xff0c;对象&#xff0c;封装&#xff0c;继承&#xff08;派生&#xff09;&#xff0c;多态的时候&#xff0c;this指针出现在成员函数中&#xff0c;并使用->成员提取符操作成员变量。在 C 中&#xff0c;每一个对象都能通…

jooq和jdbc_将jOOQ与JDBC比较

jooq和jdbc本文是我们学院课程“ jOOQ –类型安全数据库查询”的一部分 。 在SQL和特定关系数据库很重要的Java应用程序中&#xff0c;jOOQ是一个不错的选择。 当JPA / Hibernate抽象过多而JDBC过于抽象时&#xff0c;这是一种替代方法。 它显示了一种现代的领域特定语言如何可…

【lua学习】3.字符串

【lua学习】3.字符串Lua字符串的概况字符串实现字符串结构TString全局字符串表stringtable新建字符串luaS_newlstr &#xff08;先查表&#xff0c;再决定创建与否&#xff09;新建字符串 newlstr重新设置全局字符串的大小 luaS_resize全局字符串表的缩容保留字是如何不被回收的…

【lua学习】4.表

1 概述2 数据结构2.1.表Table2.2 键TKey2.3 节点&#xff08;键值对&#xff09;Node3 操作算法3.1 查找3.1.1 通用查找luaH_get3.1.2 根据字符串查找 luaH_getstr3.1.3 根据整数查找 luaH_getnum3.2 新增元素/修改元素/删除元素 luaH_set系列3.2.1 根据key获取或创建一个value…

批量提取文件创建时间_批量采集新浪微博用户内容

有时我们需要把某些用户的微博数据全部采集下来用作分析&#xff0c;每条信息复制的工作量是非常低效的&#xff0c;必须要借助工具。今天给大家介绍一款采集软件&#xff1a;微风采集器。打开软件&#xff0c;选择模板&#xff0c;下拉框选&#xff1a;批量提取指定用户微博内…