一、 注解
1. 元注解
一共有4个,常用的时候下面两个
@Target、@Rentention、@Documented、@Inherited
-
@Target
用来约束注解可以应用的地方(如方法、类、字段),比较常用的有下面几个
- TYPE:作用对象类、接口、枚举
- FIELD:成员变量
- METHOD:成员方法
- PARAMETER:方法参数
- ANNOTATION_TYPE:注解的注解
-
@Rentention
用来约束注解的生命周期,分别有三个值:
- 源码级别(source):被编译器(JVM)丢弃,不会保存在class中
- 类文件级别(class):不会加载到JVM中,保留在源码或者class中(默认也是这个)
- 运行时级别(runtime):会被JVM保留,通过反射机制读取信息
2. 注解元素
对元素的声明,采用方法的声明方式,例如:String name();
对于没有元素的注解,称为 标记注解
/**
* @author monk
* @since 2019-01-22
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
/**
* 定义注解的属性,这不是方法
*/
String name(); // 必选注解元素
int value() default 20; // 有属性就是可选属性
}
-
支持的数据类型
- 所有基本类型(int,float,boolean,byte,double,char,long,short)
- String
- Class
- enum
- Annotation
- 上述类型的数组
-
快捷方式
当注解元素是唯一1个需要赋值的,并且限定为vulue(),那么使用时候便可以使用快捷方式,省略key,直接赋值value
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @interface IntegerVaule{ int value() default 0; String name() default ""; } public class QuicklyWay{ // 当只想给value赋值时,可以使用快捷方式 @IntegerValue(10) public int age; //当name也需要赋值时必须采用key=value的方式赋值 @IntegerVaule(value = 10000,name = "MONEY") public int money; }
-
java内置注解
一共3个:
@Override @Deprecated @SuppressWarnings // 有选择的关闭编译器对类、方法、成员变量、变量初始化的警告
二、 APT
1. java编译流程
processor
反正source和compile之间,java解释器在这两者之间工作
apt
:annotation processing tool,对source源代码进行检测,找出其中Annotation,按照编写者决定生成源文件(java文件)
2. Processor
// javax.annotation.processing.Processor.java
/**
* 由JVM调用
* processingEnv:javac ProcessingEnvironment
*/
void init(ProcessingEnvironment processingEnv);
/**
* 由JVM调用,执行完init,开始执行process
*
* set:com.mono.annotaton.BindPath
*
* 第2轮筛选其依赖的注解
* roundEnv:[errorRaised=false, rootElements=[com.monk.annotationprocessor.MyGeneratedClass], processingOver=false]
*/
boolean process(Set<? extends TypeElement> annotations,RoundEnvironment roundEnv);
process函数,执行情况,从日志输出可以了解到,该函数会执行3轮
-
set包含的是annotation的注解,在第一轮时候会取出,后面两轮size=0
-
roundEnv是依赖注解使用的module的所有类,第二轮会是自己的注解类,第三轮为空,并把processingOver标记为true
-
Element:
实际上,Java 源文件是一种结构体语言,源代码的每一个部分都对应了一个特定类型的 Element ,例如包,类,字段,方法等等:
-
AbstractProcessor:
apt:annotation processing tool
对source源代码进行检测,找出其中Annotation,按照编写者决定生成源文件(java文件)
public class AptAnnotationProcessor extends AbstractProcessor { //... /** * 节点工具类(类、函数、属性都是节点) */ private Elements mElementUtils; /** * 类信息工具类 */ private Types mTypeUtils; /** * 文件生成器 */ private Filer mFiler; /** * 日志信息打印器 */ private Messager mMessager; /** * 做一些初始化的工作 * * @param processingEnvironment 这个参数提供了若干工具类,供编写生成 Java 类时所使用 */ @Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); mElementUtils = processingEnv.getElementUtils(); mTypeUtils = processingEnv.getTypeUtils(); mFiler = processingEnv.getFiler(); mMessager = processingEnv.getMessager(); } /** * 接收外来传入的参数,最常用的形式就是在 build.gradle 脚本文件里的 javaCompileOptions 的配置 * * @return 属性的 Key 集合 */ @Override public Set<String> getSupportedOptions() { return super.getSupportedOptions(); } /** * 当前注解处理器支持的注解集合,如果支持,就会调用 process 方法 * * @return 支持的注解集合 */ @Override public Set<String> getSupportedAnnotationTypes() { return super.getSupportedAnnotationTypes(); } /** * 编译当前注解处理器的 JDK 版本 * * @return JDK 版本 */ @Override public SourceVersion getSupportedSourceVersion() { return super.getSupportedSourceVersion(); } }
-
注解处理器注册
自动注册方式:
-
在当前module下的build.gradle文件下导入以下依赖
implementation 'com.google.auto.service:auto-service:1.0-rc6' annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6'
注意:以上必须同时加,否则注册不成功,我之前也踩过坑
-
在注解处理器上加上
@AutoService(Processor.class)
即可完成注册@AutoService(Processor.class) public class AptAnnotationProcessor extends AbstractProcessor { //... }
-
-
注解处理器生成类文件
通过javapoet框架编写,具体可参考上述链接使用javapoet
-
调用生成代码
编写annotationapi的android module,通过反射api,将生成的Helloworld类进行封装使用即可。
-
3. ProcessingEnvironment
由JVM初始化,可以获取:
- Filer(JavacFiler)文件管理器
- Elements,节点工具类(类、函数、属性都是节点)
- Types,类信息工具类
- Messager,日志信息打印器