每天学习一点点之注解处理器 APT

APT(Annotation Processing Tool)是一种处理注解的工具,它能够对源代码文件进行检测并找出其中的注解,然后对其进行额外的处理。由于注解处理过程是在编译时完成的,并不会影响程序的运行时性能

APT 能做什么?

APT 主要用于在编译时生成、修改或者处理代码。它可以用于创建新的源文件,但不能修改已经存在的源文件。一般有这么几种常用场景:

  1. 生成源代码:但是要注意这跟 Lombok 还是有一定区别的,Lombok 可以修改已经存在的类,这是 APT 无法做到的
  2. 编译时检查:APT 可以用于检查代码中的错误或者潜在问题。
  3. 生成文档:APT 可以用于从注解中提取信息,然后生成文档。

APT 工作流程

APT 的工作流程可以分为以下几个步骤:

  1. 收集源文件:APT 首先会收集所有的源文件。
  2. 解析源文件:然后,APT 会解析源文件,找出其中的注解。
  3. 调用注解处理器:对于每个找到的注解,APT 会调用相应的注解处理器。
  4. 生成源文件:注解处理器可以生成新的源文件。
  5. 编译源文件:最后,APT 会编译所有的源文件(包括原来的源文件和新生成的源文件)。

APT 在 Spring Boot Configuration Processor 中的使用

Spring Boot Configuration Processor是一个在编译时生成额外的元数据文件的工具,这些文件可以提供给IDE,以便在编辑 application.properties 或 application.yml 文件时提供自动完成和其他辅助功能。它也是基于 APT 在编译时扫描源代码,并生成这些元数据文件。

可以查看 ConfigurationMetadataAnnotationProcessor类,它是注解处理器,在编译时被调用:

//这个方法在每一轮注解处理中被调用。它首先找到所有的@ConfigurationProperties注解的类,然后对每一个类生成元数据	
@Overridepublic boolean process(Set<? extends TypeElement> annotations,RoundEnvironment roundEnv) {this.metadataCollector.processing(roundEnv);Elements elementUtils = this.processingEnv.getElementUtils();TypeElement annotationType = elementUtils.getTypeElement(configurationPropertiesAnnotation());if (annotationType != null) { // Is @ConfigurationProperties availablefor (Element element : roundEnv.getElementsAnnotatedWith(annotationType)) {processElement(element);}}TypeElement endpointType = elementUtils.getTypeElement(endpointAnnotation());if (endpointType != null) { // Is @Endpoint availablegetElementsAnnotatedOrMetaAnnotatedWith(roundEnv, endpointType).forEach(this::processEndpoint);}if (roundEnv.processingOver()) {try {writeMetaData();}catch (Exception ex) {throw new IllegalStateException("Failed to write metadata", ex);}}return false;}

APT 使用示例

这里基于 APT 实现一个注解,用于生成 Builder 模式代码的注解处理器。

首先需要定义一个注解:

/*** @author Dongguabai* @description* @date 2024-03-27 20:49*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface GenerateGettersSetters {
}

然后需要定义一个注解处理器。注解处理器需要继承 AbstractProcessor 类,并重写 process 方法。

/*** @author dongguabai* @date 2024-03-28 13:58*/
@SupportedAnnotationTypes("io.github.dongguabai.apt.BuilderProperty")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class BuilderProcessor extends AbstractProcessor {@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {for (Element element : roundEnv.getElementsAnnotatedWith(BuilderProperty.class)) {if (element.getKind() == ElementKind.METHOD) {ExecutableElement method = (ExecutableElement) element;String className = ((TypeElement) method.getEnclosingElement()).getSimpleName().toString();String fullClassName = ((TypeElement) method.getEnclosingElement()).getQualifiedName().toString();String packageName = ((PackageElement) method.getEnclosingElement().getEnclosingElement()).getQualifiedName().toString();String methodName = method.getSimpleName().toString();String parameterType = method.getParameters().get(0).asType().toString();try {JavaFileObject file = processingEnv.getFiler().createSourceFile(packageName + "." + className + "Builder");try (Writer writer = file.openWriter()) {writer.write("package " + packageName + ";\n\n");writer.write("public class " + className + "Builder {\n");writer.write("    private " + fullClassName + " instance = new " + fullClassName + "();\n");writer.write("    public " + className + "Builder " + methodName + "(" + parameterType + " value) {\n");writer.write("        instance." + methodName + "(value);\n");writer.write("        return this;\n");writer.write("    }\n");writer.write("    public " + fullClassName + " build() {\n");writer.write("        return instance;\n");writer.write("    }\n");writer.write("}\n");}} catch (IOException e) {processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, e.getMessage());}}}return true;}
}

我这里将这个注解打成 JAR:

➜  apt-demo git:(master) ✗ mvn clean install
...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.760 s
[INFO] Finished at: 2024-03-27T21:09:10+08:00
[INFO] Final Memory: 19M/192M
[INFO] ------------------------------------------------------------------------

User 类中使用这个注解:

/*** @author dongguabai* @date 2024-03-28 14:47*/
public class User {private String username;public String getUsername() {return username;}@BuilderPropertypublic void setUsername(String username) {this.username = username;}
}

可以看到编译后自动生成了 UserBuilder

在这里插入图片描述

总结

APT 是一个可以在编译阶段发挥作用的强大工具,它能够识别并处理源代码中的注解。APT 的主要用途包括生成新的源代码、执行编译时检查以及生成文档等。APT 能够生成新的源代码文件,但它无法修改已经存在的源文件,此外如果我们的项目需要使用 APT 生成的代码,可能还需要通过反射来处理,这其实也不太方便。

欢迎关注公众号:
在这里插入图片描述

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

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

相关文章

js的Date对象

文章目录 1. 概念2. 创建时间对象2.1. 方式一2.2. 方式二2.3. 方式三2.4. 使用场景 3. 获取年月日4. 获取时分秒5. 获取毫秒值6. 封装获取当前时间函数 1. 概念 Date 对象用于处理日期与时间。 2. 创建时间对象 2.1. 方式一 使用无参构造&#xff0c;创建出来的就是当前的时…

Facebook账号防封方法及解禁方法

Facebook作为跨境主要业务平台&#xff0c;一直以来封号率都非常高。相信点进来的各位或多或少地遇见了个人号被封&#xff0c;广告账户被禁&#xff0c;FB主页被封等情况。针对此类问题&#xff0c;今天就小编也来分享自己的Facebook防封经验。 一、Facebook被封原因 主要有以…

主流公链 - BCH BSV BTG

为什么出现分叉 BTC是自由的&#xff0c;BTC社区也是自由的&#xff0c;自然而然的会出现不同观点的群体 1. 比特币现金&#xff08;Bitcoin Cash&#xff0c;BCH&#xff09; 分叉日期&#xff1a; 2017年8月1日主要目的&#xff1a; 提高比特币的交易吞吐量和降低交易费用技术…

MTK8781安卓核心板_MT8781(Helio G99)核心板性能参数

MT8781安卓核心板搭载了八核CPU&#xff0c;其中包括两个主频高达2.2GHz的高性能Arm Cortex-A76处理器。这一处理器采用了台积电6纳米级芯片生产工艺&#xff0c;以及先进的3D图形功能的高性能Arm Mali G57级GPU。通过超快LPDDR4X内存和UFS 2.2存储供电&#xff0c;不仅提高了游…

ESCTF-密码赛题WP

*小学生的爱情* Base64解码获得flag *中学生的爱情* 社会主义核心价值观在线解码得到flag http://www.atoolbox.net/Tool.php?Id850 *高中生的爱情* U2FsdG开头为rabbit密码,又提示你密钥为love。本地toolfx密码工具箱解密。不知道为什么在线解密不行。 *大学生的爱情* …

修改nuxtjs项目中的浏览器图标步骤

处理步骤&#xff1a; 打开配置页面 使用el-upload 上传图片到后台 后台把图片转为ico&#xff0c;返回图标路径 配置页面修改本页面预览图&#xff0c;点击保存&#xff0c;修改的数据库。 通知nuxt布局页面&#xff0c;修改head节点中的图标属性&#xff0c;…

【笔记】RDD算子操作(Spark基础知识)

持续更新中&#xff01;&#xff01;&#xff01; 目录 一、RDD的创建 1.从本地创建 &#xff08;1&#xff09;本地文件 &#xff08;2&#xff09;hdfs文件&#xff08;先提前创建目录并上传文件&#xff09; 2.从集合创建&#xff08;通过并行集合&#xff08;列表&am…

Java服务运行在Linux----维护常用命令

想起来哪些再添加上去 查看Java程序进程 jps -l 查出进程后根据pid 查询程序所在目录 pwdx 31313 根据端口查找PID 根据pid杀死程序 kill -p 31313 查看目录下所有包含9527的文件 grep -rn 9527 查看磁盘空间 查找文件名"nginx"文件或模糊查找"*nginx*&quo…

yolov8逐步分解(1)--默认参数超参配置文件加载

本章节将介绍yolov8算法训练过程中的第一步&#xff1a;加载默认训练参数以及超参的配置文件default.yaml。 Yolov8 训练的入口文件为train.py&#xff0c;文件结构如下图所示&#xff1a; 1. 执行train函数&#xff0c;函数默认配置参数为cfgDEFAULT_CFG &#xff0c…

Ubuntu通过分用户进行多版本jdk配置

前言&#xff1a;本文内容为实操记录&#xff0c;仅供参考&#xff01; linux安装jdk参考&#xff1a;http://t.csdnimg.cn/TeECj 出发点&#xff1a;最新的项目需要用jdk17来编译&#xff0c;就把服务器的jdk版本升级到了17&#xff0c;但是有一些软件例如nexus还需要jdk1.8进…

新体验、高效能,星河零代码产线加速带动产业新质生产力

2023年12月&#xff0c;在Wave Summit深度学习开发者大会上&#xff0c;飞桨隆重推出了端云协同的低代码开发工具——PaddleX。这款一站式AI开发工具集成了飞桨开发套件多年积累的模型训练、推理全流程开发的优势能力。同时立足产业真实应用场景&#xff0c;内置12个面向产业应…

Linux系统使用Docker部署MinIO结合内网穿透实现公网访问本地存储服务

文章目录 前言1. Docker 部署MinIO2. 本地访问MinIO3. Linux安装Cpolar4. 配置MinIO公网地址5. 远程访问MinIO管理界面6. 固定MinIO公网地址 前言 MinIO是一个开源的对象存储服务器&#xff0c;可以在各种环境中运行&#xff0c;例如本地、Docker容器、Kubernetes集群等。它兼…