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

algorunner
• 阅读 646

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

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

什么是注解
注:因为APT = 注解+ 注解处理器(AbstractProcessor)。因此需要了解什么是注解,不过对于java开发人员来说,注解应该是耳熟能详了,这边就不再论述。如果不了解啥是注解的小伙伴,可以查看如下文章科普一下
baike.baidu.com/item/%E6%B3…
这边得特别说下元注解@Retention

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

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

1、常用元素

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

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

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

2、Element元素常用变量

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

更多element详细内容可以查看如下链接
www.jianshu.com/p/899063e84…
创建注解处理器步骤

创建注解类
创建一个继承自 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文件

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

文件内容如下

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的简单版本

点赞
收藏
评论区
推荐文章
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
7个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Wesley13 Wesley13
3年前
java mvc 新趋势——从运行期间类扫描到编译期间
简介今天我要讲解的是主角是AnnotationProcessor,她不是什么新技术jdk1.6就存在了。AnnotationProcessor是javac的一个工具,它用来在编译时扫描和处理注解。通过AnnotationProcessor可以获取到注解和被注解对象的相关信息,然后根据注解自动生成Java代码,省去了手动编写,
kenx kenx
4年前
Java自定义注解使用和详解
前言我们在做开发springboot项目时候会遇到各种各样注解,使用各种各样注解,极大的简便了我们开发流程,方式,从JDK5开始支持注解是Java语言的一种强大的功能可以理解为代码上的特殊标记,通过这些标记我们可以在编译,类加载,运行等程序类的生命周期内被读取、执行相应的处理。通过注解开发人员可以在不改变原有代码和逻辑的情况下在源代码中嵌入补充信息自定
Wesley13 Wesley13
3年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Stella981 Stella981
3年前
Lombok经常用,但是你知道它的原理是什么吗?(二)
在上一篇Lombok经常用,但是你知道它的原理是什么吗?(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fjuejin.im%2Fpost%2F5e54d38a6fb9a07cbf46b3ca)简单介绍了注解处理器,是用来处理编译期的注解的一个工具,我们只是自己生成了一些代码,但是和Lo
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Easter79 Easter79
3年前
Spring注解@Resource和@Autowired区别对比、spring扫描的默认bean的Id、程序获取spring容器对象
\注解扫面的bean的ID问题0.前提需要明白注解扫描出来的bean的id默认是类名首字母小写,当然可以指定id:
Stella981 Stella981
3年前
EventBus3.0 混淆解决方案
EventBus3.0采用注解处理,为了提高效率可在编译时通过注解处理器生成索引代码。但是如果使用索引的话就不能混淆被Subscribe注解的方法。为了能够提高效率使用索引并且混淆被Subscribe注解的方法可以使_gradle.plugin.greenrobot.eventbus_插件\TOC\gradle.plugin.gre
从原理聊JVM(五):JVM的编译过程和优化手段 | 京东云技术团队
一、前端编译前端编译就是将Java源码文件编译成Class文件的过程,编译过程分为4步:1准备初始化插入式注解处理器(AnnotationProcessingTool)。2解析与填充符号表将源代码的字符流转变为标记(Token)集合,构造出抽象语法树(AS
警惕!自定义注解使用不当的排查实录
一、引言大家好,在日常开发过程中,Java注解(Annotation)是开发中经常使用的一个手段,用于给代码添加元数据的标记。它们可以提供代码额外的信息,这些信息可以在编译时或运行时被访问。注解不会改变代码的执行逻辑,但可以被编译器、JVM或框架等工具用于
algorunner
algorunner
Lv1
山无棱天地合,不服咱俩使劲磕。
文章
3
粉丝
0
获赞
0