Java注解编译期处理AbstractProcessor详解
概述
我们接触的注解主要分为以下两类
- 运行时注解:通过反射在运行时动态处理注解的逻辑
- 编译时注解:通过注解处理器在编译期动态处理相关逻辑
平时我们接触的框架大部分都是运行时注解,比如:@Autowire`` @Resoure @Bean 等等。
那么我们平时有接触过哪些编译期注解呢,@Lombok @AutoService 等等
像这些编译时注解的作用都是自动生成代码,一是为了提高编码的效率,二是避免在运行期大量使用反射,通过在编译期利用反射生成辅助类和方法以供运行时使用。
那这些编译器注解都是如何运行的呢? 又是怎么自动生成代码的呢?
我们今天来详细介绍一下,不过再介绍之前,可以先简单了解一下Java注解的基本概念
注解处理器
注解处理流程
注解编译期处理流程最关键的一个类就是Processor ,它是注解处理器的接口类,我们所有需要对编译期处理注解的逻辑都需要实现这个Processor接口,当然,AbstractProcessor 抽象类帮我们写好了大部分都流程,所以我们只需要实现这个抽象类就可以很方便的定义一个注解处理器;
注解处理流程由多轮完成。每一轮都从编译器在源文件中搜索注解并选择适合这些注解的 注解处理器(AbstractProcessor) 开始。每个注解处理器依次在相应的源上被调用。
如果在此过程中生成了任何文件,则将以生成的文件作为输入开始另一轮。这个过程一直持续到处理阶段没有新文件生成为止。
注解处理器的处理步骤:
- 在java编译器中构建;
- 编译器开始执行未执行过的注解处理器;
- 循环处理注解元素(Element),找到被该注解所修饰的类,方法,或者属性;
- 生成对应的类,并写入文件;
- 判断是否所有的注解处理器都已执行完毕,如果没有,继续下一个注解处理器的执行(回到步骤1)。
AbstractProcessor
这是注解处理器的核心抽象类,我们主要来看看里面的方法
getSupportedOptions()
默认的实现是 从注解 SupportedOptions 获取值,该值是一个字符数组,例如
@SupportedOptions({"name","age"})
public class SzzTestProcessor extends AbstractProcessor {
}
不过貌似该接口并没有什么用处。
有资料表示 该可选参数可以从processingEnv获取到参数。
String resultPath = processingEnv.getOptions().get(参数);
实际上这个获取的参数是编译期通过入参 -Akey=name 设置的,跟getSupportedOptions没有什么关系。
getSupportedAnnotationTypes
获取当前的注解处理类能够处理哪些注解类型,默认实现是从 SupportedAnnotationTypes 注解里面获取;
注解值是个字符串数组 String [] ;
匹配上的注解,会通过当前的注解处理类的 process方法传入。
例如下面使用 * 通配符支持所有的注解
@SupportedAnnotationTypes("*")
@SupportedSourceVersion(SourceVersion.RELEASE_11)
public class PrintingProcessor extends AbstractProcessor {
}
又或者可以直接重写这个接口
@Override
public ImmutableSet<String> getSupportedAnnotationTypes() {
return ImmutableSet.of(AutoService.class.getName());
}
最终他们生效的地方就是用来做过滤,因为处理的时候会获取到所有的注解,然后根据这个配置来获取自己能够处理的注解。
getSupportedSourceVersion
获取该注解处理器最大能够支持多大的版本,默认是从注解 SupportedSourceVersion中读取,或者自己重写方法,如果都没有的话 默认值是 RELEASE_6
@SupportedSourceVersion(SourceVersion.RELEASE_11)
public class PrintingProcessor extends AbstractProcessor {
}
或者重写(推荐 , 获取最新的版本)
@Override
public SourceVersion getSupportedSourceVersion() {
// 设置为能够支持最新版本
return SourceVersion.latestSupported();
}