聊聊如何运用JAVA注解处理器(APT)

news2024/12/24 11:40:38

什么是APT

APT(Annotation Processing Tool)它是Java编译期注解处理器,它可以让开发人员在编译期对注解进行处理,通过APT可以获取到注解和被注解对象的相关信息,并根据这些信息在编译期按我们的需求生成java代码模板或者配置文件(比如SPI文件或者spring.fatories)等。APT获取注解及生成代码都是在代码编译时候完成的,相比反射在运行时处理注解大大提高了程序性能

APT的工作流程

什么是注解

:因为APT = 注解+ 注解处理器(AbstractProcessor)。因此需要了解什么是注解,不过对于java开发人员来说,注解应该是耳熟能详了,这边就不再论述。如果不了解啥是注解的小伙伴,可以查看如下文章科普一下

https://baike.baidu.com/item/%E6%B3%A8%E8%A7%A3/22344968

这边得特别说下元注解@Retention


因为APT是在java编译器使用,因此@Retention的value通常指定为source或者class,这样可以提高一点性能。就我个人而言,我倾向指定为source

APT之Element常用元素以及Element元素常用变量

1、常用元素


这些元素映射到java,我通过一个例子大家应该就可以了解这些元素是指什么

2、Element元素常用变量


更多element详细内容可以查看如下链接

https://www.jianshu.com/p/899063e8452e

创建注解处理器步骤

  • 创建注解类
  • 创建一个继承自 AbstractProcessor 的类,这就是 APT 的核心类
  • 注册处理器

创建注解处理器示例

注: 示例要实现的功能,通过一个自定义注解AutoComponent,通过注解处理器扫描解析AutoComponent注解,并生成lybgeek.components,spring通过解析lybgeek.components,实现bean注册

1、创建注解类

@Documented
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface AutoComponent {

}

2、创建一个继承自 AbstractProcessor 的类

这边需介绍这个类里面几个核心的方法

 public synchronized void init(ProcessingEnvironment processingEnv)

init方法可以让我们处理器的初始化阶段,通过ProcessingEnvironment来获取一些帮助我们来处理注解的工具类

// Element操作类,用来处理Element的工具
Elements elementUtils = processingEnv.getElementUtils();

// 类信息工具类,用来处理TypeMirror的工具
Types typeUtils = processingEnv.getTypeUtils();

// 日志工具类,因为在process()中不能抛出一个异常,那会使运行注解处理器的JVM崩溃。所以Messager提供给注解处理器一个报告错误、警告以及提示信息的途径,用来写一些信息给使用此注解器的第三方开发者看
Messager messager = processingEnv.getMessager();

// 文件工具类,常用来读取或者写资源文件
Filer filer = environment.getFiler();

public Set<String> getSupportedAnnotationTypes()

getSupportedAnnotationTypes方法用来指定需要处理的注解集合,返回的集合元素需要是注解全路径(包名+类名)

public SourceVersion getSupportedSourceVersion()

getSupportedSourceVersion方法用来指定当前正在使用的Java版本,一般返回SourceVersion.latestSupported()表示最新的java版本即可

    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv)

process是注解处理器核心方法,注解的处理和生成代码或者配置资源都是在这个方法中完成。

Java官方文档给出的注解处理过程的定义:注解处理过程是一个有序的循环过程。在每次循环中,一个处理器可能被要求去处理那些在上一次循环中产生的源文件和类文件中的注解。

每次循环都会调用process方法,process方法提供了两个参数,第一个是我们请求处理注解类型的集合(也就是我们通过重写getSupportedAnnotationTypes方法所指定的注解类型),第二个是有关当前和上一次循环的信息的环境。返回值表示这些注解是否由此 Processor 声明,如果返回 true,则这些注解已声明并且不要求后续 Processor 处理它们;如果返回 false,则这些注解未声明并且可能要求后续 Processor 处理它们。

核心方法介绍完后,我们通过示例来自定义一个注解处理器

@AutoService(Processor.class)
@SupportedOptions("debug")
public class AutoComponentProcessor extends AbstractComponentProcessor {
    
    /**
     * 元素辅助类
     */
    private Elements elementUtils;

    private Set<String> componentClassNames = new ConcurrentSkipListSet<>();

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        elementUtils = processingEnv.getElementUtils();
    }


    @Override
    public Set<String> getSupportedAnnotationTypes() {
        return Collections.singleton(AutoComponent.class.getName());
    }


    @Override
    protected boolean processImpl(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
       // 注解处理完成,创建配置文件
        if (roundEnv.processingOver()) {
            generateConfigFiles();
        } else {
            processAnnotations(annotations, roundEnv);
        }
        return false;
    }

3、注册处理器

因为处理器是通过SPI机制实现,因此它的注册,其实就是在META-INF/services底下创建javax.annotation.processing.Processor文件,文件内容为自定义的处理器类

com.github.lybgeek.apt.process.AutoComponentProcessor

不过我们可以在项目的POM中引入GAV

 <dependency>
            <groupId>com.google.auto.service</groupId>
            <artifactId>auto-service</artifactId>
            <version>1.0.1</version>
            <scope>provided</scope>
        </dependency>

或者

<dependency>
            <groupId>net.dreamlu</groupId>
            <artifactId>mica-auto</artifactId>
            <version>2.3.0</version>
            <scope>provided</scope>
        </dependency>

在process的处理器上,加上注解

@AutoService(Processor.class)

就会在编译期自动生成spi配置文件,它实现机制也是采用APT

4、当我们制作好处理器后,我们可以将处理器打成jar,提供给项目用

示例

<dependency>
            <groupId>${project.groupId}</groupId>
            <artifactId>springboot-apt-framework</artifactId>
            <version>${project.version}</version>
        </dependency>

在项目编译后,就会在target的MATA-INF底下看到lybgeek.components文件


文件内容如下

# Generated by LYB-GEEK AT TIME : 2023-01-12T17:14:24.982
com.github.lybgeek.test.service.EchoService
com.github.lybgeek.test.service.HelloService

接下来就是解析lybgeek.components,并通过spring提供的扩展点和API进行bean注册,因为这块内容不属于APT的内容,本文就不再论述,对这部分感兴趣的朋友,可以通过文末提供的demo链接查看

总结

在未接触APT之前,我们可能会通过反射去解析注解并实现功能,接触APT之后,我们又多了额外一种比反射更能提升性能的实现实现。不过任何东西都有其适用场景,APT主要还是用在编译期帮我们生成代码或者配置等,如果我们项目要使用APT生成的代码,有可能还是需要通过反射处理。

我们耳熟能详的lombok、mapstruct、包括spring5.0之后提供的@Index都是通过APT来实现,文中的示例其实就是仿造spring index来实现,可以看成是spring index的简单版本

demo链接

https://github.com/lyb-geek/springboot-learning/tree/master/springboot-apt

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

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

相关文章

基于DistFlow的含分布式电源配电网优化模型【IEEE39节点】(Python代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

SpringBoot【基础篇】---- SSMP整合综合案例

SpringBoot【基础篇】---- SSMP整合综合案例1. 模块创建2. 实体类开发3. 数据层开发----基于CRUD查看MP运行日志查看 MP 的运行日志4. 数据层开发----分页功能制作5. 数据层开发----条件查询功能制作6. 业务层开发业务层快速开发7. 表现层开发8. 表现层消息一致性处理9. 前后端…

STC32G单片机内置ADC及应用编程

一 STC32G单片机内置ADC模块简介 STC32G单片机内部集成了一个12位高速ADC转换器&#xff0c;ADC的最高时钟频率为系统频率的1/2。其输入通道多达15个&#xff08;第15通道为专门测量内部1.19V参考信号源的通道&#xff09;&#xff0c;可分时切换使用。 STC15系列单片机内置AD…

AES加密

来源&#xff1a;链接: b站up主可厉害的土豆 &#xff08;据评论区说图片中有计算错误&#xff0c;但是过程是对的。只是了解过程问题不大&#xff0c;专门研究这一块的话自己可以看视频算一下&#xff09; 准备 首先&#xff0c;明文是固定长度 16字节 128位。 密钥长度可以…

C++语法(18)---- set和map

C语法&#xff08;17&#xff09;---- 二叉搜索树_哈里沃克的博客-CSDN博客https://blog.csdn.net/m0_63488627/article/details/130174864 目录 1.set的介绍 1.set使用 1.基本结构 2.insert 3.erase 4.find 5.count 2.multiset 1.count 2.find 2.map的介绍 1.map …

zookeeper + kafka集群搭建详解

目录 1.消息队列介绍 1.为什么需要消息队列 &#xff08;MO&#xff09; 2.使用消息队列的好处 3.消息队列的两种模式 2.Kafka相关介绍 1.Kafka定义 2.Kafka简介 3. Kafka的特性 3.Kafka系统架构 1. Broker&#xff08;服务器&#xff09; 2. Topic&#xff08;一个队…

GaussDB数据库存储过程介绍

文章目录一、前言二、GaussDB中的定义三、存储过程的使用场景四、存储过程的使用优缺点五、存储过程的示例及示例解析1、GaussDB存储过程语法格式2、GaussDB存储过程语法示例3、存储过程的调用方法七、总结一、前言 华为云数据库GaussDB是一款高性能、高安全性的云原生数据库&…

链表基础知识

1.链表必知必会 什么是链表? 链表是一种通过指针串联在一起的线性结构&#xff0c;每一个节点由两部分组成&#xff0c;一个是数据域一个是指针域&#xff08;存放指向下一个节点的指针&#xff09;&#xff0c;最后一个节点的指针域指向null&#xff08;空指针的意思&#…

23北京邮电大学备考经验

目录【写在前面】本科成绩择校历程英语复习数学复习政治复习专业课复习其它建议笔记复盘压力处理恋爱关系【写在最后】【写在前面】 初试成绩&#xff1a; 本科成绩 总体&#xff1a;浙江某双非学校的软件工程专业、综合测评成绩班级前两名、浙江省省级优秀毕业生、发表过论…

【Node】Node.js 资源汇总推荐

【导读】&#xff1a;Node.js 是一个开源、跨平台的&#xff0c;用于编写服务器和命令行的 JavaScript 运行时工具。awesome-nodejs 是sindresorhus发起维护的 Node.js 资源列表&#xff0c;内容包括&#xff1a;命令行工具、日志、调试、HTTP、构建工具、文件系统、模板、Web …

Elasticjob(2.1.4) failover 、misfire及执行线程池分析

Failover 当设置failover为true时候&#xff0c;elasticjob 集群通过zookeeper 的event watcher 监听是否有instance 丢失&#xff0c;然后对丢失instance 对应的分片进行立即执行。重复一下&#xff0c;failover是立即执行&#xff0c;不是按crontab时间来触发&#xff0c;这…

基于RDF本体模型和图数据库实现知识查询与推理

基于RDF本体模型和图数据库实现知识查询与推理 基于RDF本体模型和图数据库实现知识查询与推理一、案例本体模型解释二、数据构建与查询 Here’s the table of contents: 基于RDF本体模型和图数据库实现知识查询与推理 本文主要使用ONgDB图数据库和Neosemantics组件&#xff0c;…

自建个人音乐播放器Navidrome - 内网穿透实现在外随时访问

文章目录 1. 前言2. Navidrome网站搭建2.1 Navidrome下载和安装2.1.1 安装并添加ffmpeg2.1.2下载并配置Navidrome2.1.3 添加Navidrome到系统服务 2.2. Navidrome网页测试 3. 本地网页发布3.1 cpolar的安装和注册3.2 Cpolar云端设置3.3 Cpolar本地设置 4. 公网访问测试5. 结语 转…

【Android实战开发】flutter实现网络请求的方法示例

Flutter网络请求使用的是Dio。Dio是一个强大易用的dart http请求库&#xff0c;支持Restful API、FormData、拦截器、请求取消、Cookie管理、文件上传/下载……. Flutter json数据解析是使用了json_serializable package包。它是一个自动化源代码生成器&#xff0c;可以为我们…

C++快速幂详解例题

基本概念 什么是快速幂呢&#xff1f;个人理解&#xff0c;就是更快速的计算幂运算。 比如计算a^b 刚学这个算法的时候我也很疑惑&#xff0c;幂运算不是有现成的公式么&#xff0c;直接pow&#xff08;a,b&#xff09;不就好了吗&#xff1f; 后来才明白&#xff0c;pow(a,b)的…

三分钟了解什么是时序数据库

在介绍时序数据库之前&#xff0c;我们先来看看什么是时序数据。时序数据就是基于时间排序的数据&#xff0c;再通过时间坐标将这些数据连接起来&#xff0c;形成一个折线图&#xff0c;直观地展示一个指标在过去一段时间内的走势和规律&#xff0c;帮助定位数据异常点。 时序…

Oracle中Archived redolog的生成

目录 一、问题预览 二、问题解答 一、问题预览 大家都知道 Oracle 中 online redolog切换后会生成 archived redolog&#xff0c;心里默认的就是 online redolog 切换后 archived redolog 已经生成。切换示意图&#xff0c;如下图所示。 但事实真的是这样吗&#xff1f; 二、…

C++ 23 实用工具(一)

C 23 实用工具&#xff08;一&#xff09; 工具函数是非常有价值的工具。它们不仅可以用于特定的领域&#xff0c;还可以应用于任意值和函数&#xff0c;甚至可以创建新的函数并将它们绑定到变量上。 常用函数 你可以使用各种变体的 min、max 和 minmax 函数来对值和初始化列…

【使用ChatGPT自动化】批量转换.xls文件为.xlsx文件

第1次提问&#xff1a; 我&#xff1a;我想使用Python批量转换.xls文件为.xlsx文件&#xff0c;请你提供代码 它&#xff1a; 当涉及到批量处理文件时&#xff0c;我们通常需要使用Python中的os模块和glob模块。os模块用于管理文件和目录&#xff0c;glob模块用于匹配文件路径名…

Visual Studio Code 1.77 发布!

欢迎使用 Visual Studio Code 2023 年3月版。此版本有许多更新&#xff0c;其中一些主要亮点包括&#xff1a; 无障碍改进&#xff1a;新的悬停、通知和 Sticky Scroll 快捷键 复制 GitHub 深度链接&#xff1a;在编辑器内创建永久链接和 HEAD 链接 笔记本保存格式&#xff1…