Java注释处理器

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

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

目录

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

1.简介

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

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

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

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

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

相关文章

浅谈“PHP multipart/form-data 远程DOS漏洞”

一&#xff1a;漏洞名称&#xff1a; PHP multipart/form-data 远程DOS漏洞 描述&#xff1a; PHP 在处理HTTP请求中的multipart/form-data头部数据时存在一个安全漏洞&#xff0c;导致PHP大量重复分配和拷贝内存的操作&#xff0c;可能造成CPU资源占用100%并持续较长时间&…

java后端工程师平时开发或多或少会用到Myeclipse,那么它有哪些快捷键呢

孙叫兽,前端全栈工程师,微信公众号:电商程序员,主页QQ群有Myeclipse安装包。 下载地址:http://www.genuitec.com/products/myeclipse/download/ MyEclipse,是在Eclipse 基础上加上自己的插件开发而成的功能强大的企业级集成开发环境,主要用于Java、Java EE以及移动应用的…

加密编码类型的密文特征分析

转载https://blog.csdn.net/weixin_45728976/article/details/109219997 MD5、sha1、HMAC算法、NTLM等相似加密类型 1、MD5——示例21232F297A57A5A743894A0E4A801FC3 一般MD5值是32位由数字“0-9”和字母“a-f”所组成的字符串&#xff0c;如图。如果出现这个范围以外的字符…

孙叫兽进阶之路之如何进行情绪管理

程序员不光有硬实力&#xff0c;更要有软实力。 程序员文武双全之道。 ---孙叫兽&#xff0c;前端全栈程序员&#xff0c;微信公众号&#xff1a;电商程序员。 情绪的作用&#xff0c;存在于我们每天的工作和生活中&#xff0c;无时不刻地影响着人们的思想和行为。如何控制和管…

验证码(captcha)的由来

如果您允许用户在您的网站上发表内容&#xff0c;如留下评论和创建用户配置文件&#xff0c;那么您可能会看到&#xff0c;垃圾留言散播者试图利用这些渠道来给他们自己的网站创造流量。在您的网站上出现这类垃圾留言&#xff0c;对任何人来说都不愉快。用户可能遭遇恼人的广告…

javaone_JavaOne正在重建动力

javaone在JavaOne上度过了一个非常忙碌的一周&#xff0c;今年的活动有很多让人喜欢的地方。 有很多惊喜的公告&#xff0c;很多很好的内容/会议&#xff0c;并且在场地和组织上都有很多改进。 对于一直耐心等待我发表所有演讲的人们&#xff0c;我为您的延迟表示歉意……给4个…

夜神模拟器安装证书之burp抓包

安装步骤 1.http 不需要安装证书即可抓app包 2.https 需要安装证书 一、设置burp代理 注意IP是本机下的ip 二、模拟器设置代理 点击wifi长按鼠标修改网络 三、导出CA证书 模拟器中进入http://burp页面&#xff0c;点击黄色的地方下载 下载证书&#xff0c;可以选择本…

接手一个项目,后缀名为.bak文件,原来它是这个意思

.bak是备份文件&#xff0c;为文件格式扩展bai名&#xff0c;这类文件一du般在.bak前面加上应该有zhi原来的扩展名比如windows.dll.bak&#xff0c;或是windows_dll.bak&#xff0c;有dao的则是由原文件的后缀名和bak混合而成&#xff0c;如proteus的备份文件为.DBK。很多软件,…

java大佬是如何快速配置IntelliJ IDEA的Tomcat及安装配置Tomcat及java开发环境

孙叫兽,前端全栈工程师,java工程师。编译器及工具可以在主页QQ群群文件获取。 JDK 可以到官网下载:http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html Tomcat下载:Http://tomcat.apache.org/ IntelliJ IDEA下载:https://www.jetbrain…

mysqldatadir 转移

当mysql data路径与原始目录不一致时 &#xff0c;请在mysql 安装目录下my-default.ini 进行设置&#xff0c;取消对应#注释的地址&#xff0c;设置新地址&#xff0c;保存&#xff0c;重新启动&#xff0c;即可。 从网上各种搜索啊&#xff0c;各种尝试&#xff0c;都不能解决…

Tomcat的默认连接器

Tomcat有几个连接器可供选择。 我将把APR连接器放在一边&#xff0c;而将重点放在BIO和NIO上。 BIO连接器&#xff08;阻塞I / O&#xff09;正在阻塞–它使用线程池&#xff0c;每个线程在该线程池中接收请求&#xff0c;处理请求&#xff0c;响应并返回到该池。 在阻塞操作期…

应急响应 WEB 分析日志攻击,后门木马(手动分析 和 自动化分析.)

转载文章&#xff1a;https://blog.csdn.net/weixin_54977781/article/details/124976164?spm1001.2100.3001.7377&utm_mediumdistribute.pc_feed_blog_category.none-task-blog-classify_tag-17-124976164-null-null.nonecase&depth_1-utm_sourcedistribute.pc_feed_…

thinkphp第一节结构

thinkphp 网站:http://thinkphp.cn 输入网址&#xff1a;localhost:8080/demo/ 则说明thinkphp安装成功 目录结构如下&#xff1a; application子目录结构&#xff1a; common&#xff1a;公用文件目录 home&#xff1a;Home模板 Runtime&#xff1a;记录运行信息 home目录下&a…

孙叫兽进阶之路之软件开发生命周期

孙叫兽,前端全栈开发工程师,java工程师。 软件开发生命周期:

并发基础知识:死锁和对象监视器

本文是我们学院课程中名为Java Concurrency Essentials的一部分 。 在本课程中&#xff0c;您将深入探讨并发的魔力。 将向您介绍并发和并发代码的基础知识&#xff0c;并学习诸如原子性&#xff0c;同步和线程安全之类的概念。 在这里查看 &#xff01; 目录 1.活泼 1.1。 …

常用的30+种未授权访问漏洞汇总

未授权访问漏洞汇总预览 1 、FTP 未授权访问&#xff08;21&#xff09; 2 、LDAP 未授权访问&#xff08;389&#xff09; 3 、Rsync 未授权访问&#xff08;873&#xff09; 4 、ZooKeeper 未授权访问&#xff08;2181&#xff09; 5 、Docker 未授权访问&#xff08;2375&a…

孙叫兽进阶之路之敏捷开发

孙叫兽,前端全栈工程师,java工程师,技术交流请添加主页QQ群,微信公众号:电商程序员

四:理解Page类的运行机制(例:基于PageStatePersister的页面状态存取)

有人说类似gridview datalist这样的控件最好不要用在高并发,IO大的网站中企业应用中为了快速开发到可以用一用因为这是一类"沉重"的组件我们姑且不谈这种看法的正确性(我个人觉得有道理)只谈它为什么笨重:因为这些控件给页面带来了大量的viewstate<input type&quo…

Fiddler使用教程

一、简介及原理 1.1 Fiddler简介 Fiddler 是一个 HTTP 协议调试代理工具&#xff0c;它能够记录并检查所有你的电脑和互联网之间的 HTTP 通讯。Fiddler 提供了电脑端、移动端的抓包、包括 http 协议和 https 协议都可以捕获到报文并进行分析&#xff1b;可以设置断点调试、截取…

js 引入 缓存_引入故意缓存

js 引入 缓存几周前&#xff0c;我参加了ThoughtWorks 技术雷达研讨会。 我在ThoughtWorks工作了多年&#xff0c;并认为如果有人知道这些人在软件开发方面的发展趋势如何。 在技​​巧上带有上升箭头的数字中&#xff0c;第17位被称为“周到缓存”。 和斯科特肖一起喝酒时&…