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

demo链接

github.com/lyb-geek/sp…