注解系列
注解基础
APT
JavaPoet
前1篇介绍了注解的基本知识和常见用法,由于运行期(RunTime)利用反射去获得信息还是比较消耗性能的,本篇将介绍1种使用注解更加优雅的方式,编译期(Compile time)注解,和处理编译期注解的手段APT和Javapoet,限于篇幅,本篇侧重介绍APT
首先你的注解需要声明为CLASS
@Retention(RetentionPolicy.CLASS)
编译期解析注解基本原理:
在某些代码元素上(如类型、函数、字段等)添加注解,在编译时编译器会检查AbstractProcessor的子类,并且调用该类型的process函数,然后将添加了注解的所有元素都传递到process函数中,使得开发人员可以在编译器进行相应的处理,例如,根据注解生成新的Java类,这也就是ButterKnife等开源库的基本原理。
在处理编译器注解的第1个手段就是APT(Annotation Processor Tool),即注解处理器。在java5的时候已存在,但是java6开始的时候才有可用的API,最近才随着butterknife这些库流行起来。本章将论述甚么是注解处理器,和如何使用这个强大的工具。
甚么是APT
APT是1种处理注解的工具,确切的说它是javac的1个工具,它用来在编译时扫描和处理注解,1个注解的注解处理器,以java代码(或编译过的字节码)作为输入,生成.java文件作为输出,核心是交给自己定义的处理器去处理,
如何使用
每一个自定义的处理器都要继承虚处理器,实现其关键的几个方法
public class MyProcessor extends AbstractProcessor {
@Override
public synchronized void init(ProcessingEnvironment env){ }
@Override
public boolean process(Set<? extends TypeElement> annoations, RoundEnvironment env) { }
@Override
public Set<String> getSupportedAnnotationTypes() { }
@Override
public SourceVersion getSupportedSourceVersion() { }
}
下面重点介绍下这几个函数:
init(ProcessingEnvironment env)
: 每个注解处理器类都必须有1个空的构造函数。但是,这里有1个特殊的init()方法,它会被注解处理工具调用,并输入ProcessingEnviroment参数。ProcessingEnviroment提供很多有用的工具类Elements, Types和Filerprocess(Set<? extends TypeElement> annotations, RoundEnvironment env)
: 这相当于每一个处理器的主函数main()。你在这里写你的扫描、评估和处理注解的代码,和生成Java文件。输入参数RoundEnviroment,可让你查询出包括特定注解的被注解元素。这是1个布尔值,表明注解是不是已被处理器处理完成,官方原文whether or not the set of annotations are claimed by this processor
,通常在处理出现异常直接返回false、处理完成返回true。getSupportedAnnotationTypes()
: 必须要实现;用来表示这个注解处理器是注册给哪一个注解的。返回值是1个字符串的集合,包括本处理器想要处理的注解类型的合法全称。getSupportedSourceVersion()
: 用来指定你使用的Java版本。通常这里返回SourceVersion.latestSupported(),你也能够使用SourceVersion_RELEASE_6、7、8
由于处理器是javac的工具,因此我们必须将我们自己的处理器注册到javac中,在之前我们需要提供1个.jar文件,打包你的注解处理器到此文件中,并在在你的jar中,需要打包1个特定的文件 javax.annotation.processing.Processor到META-INF/services路径下
把MyProcessor.jar放到你的builpath中,javac会自动检查和读取javax.annotation.processing.Processor中的内容,并且注册MyProcessor作为注解处理器。
超级麻烦有木有,不过不要慌,谷歌baba给我们开发了AutoService注解,你只需要引入这个依赖,然后在你的解释器第1行加上
@AutoService(Processor.class)
然后就能够自动生成META-INF/services/javax.annotation.processing.Processor文件的。省去了打jar包这些繁琐的步骤。
APT中的Elements和TypeMirrors
在前面的init()中我们可以获得以下援用
在注解处理进程中,我们扫面所有的Java源文件。源文件的每个部份都是1个特定类型的Element
先来看1下Element
对编译器来讲 代码中的元素结构是基本不变的,如,组成代码的基本元素包括包、类、函数、字段、变量的等,JDK为这些元素定义了1个基类也就是Element
类
Element有5个直接子类,分别代表1种特定类型
==
PackageElement | 表示1个包程序元素,可以获得到包名等 |
---|---|
TypeParameterElement | 表示1般类、接口、方法或构造方法元素的泛型参数 |
TypeElement | 表示1个类或接口程序元素 |
VariableElement | 表示1个字段、enum 常量、方法或构造方法参数、局部变量或异常参数 |
开发中Element可根据实际情况强转为以上5种中的1种,它们都带有各自独有的方法,以下所示
package com.example; // PackageElement
public class Test { // TypeElement
private int a; // VariableElement
private Test other; // VariableElement
public Test () {} // ExecuteableElement
public void setA ( // ExecuteableElement
int newA // TypeElement
) {}
}
再举个栗子
上一篇 java 场景总结(二)