logo头像

我有一个梦想

注解原理

本文于 909 天之前发表,文中内容可能已经过时。

[TOC]

注解

简析

元注解:修饰注解的注解,

  • @Target:注解的作用目标(修饰方法,类还是字段)
  • @Retention:注解的生命周期
    • SOURCE:仅存在java源文件中,经过编译器后就丢弃,适用于一些检查行的操作,比如@Override
    • CLASS:编译class文件时生效,适用于在编译时女性一些预处理操作,比如Butterknife的@BindView,在编译时,通过注解器生成一些辅助代码,完成完整的功能
    • RUNTIME:保留在运行时VM中可以通过反射获取注解。适用于一些需要运行时动态获取注解信息,类似反射获取注解等,比如EventBus的@Subscribe
  • @Documented:注解是否应当被包含在JavaDoc文档中
  • @Inherited:是否允许子类继承该注解
  • AnnotationInvocationHandler:专门处理注解的Handler

代码的生命周期包含:编码(SOURCE)—->编译(CLASS)—->运行(RUNTIME)

默认时注解在编译阶段,即CLASS阶段

本质:一个继承了Annotation接口的接口

  • 运行时处理:使用反射获取当前的所需要的东西
  • 编译时处理:APT技术,即编译期扫描java文件的注解,并传递到注解处理器,注解处理器可根据注解生成新的java文件

注解器

注解器通常是以Java代码(或者编译过的字节码)作为输入,生成.java文件作为输出

image

使用google的AutoService(@AutoService)可以自动生成resources/META-INF.services中的注册目录

image

包含注解处理器,注解声明库,实际使用APT的Android/Java项目

为什么把注解处理器独立抽成一个库呢?

对于Android项目默认是不包含 APT相关类的。所以要使用APT技术,那么就必须创建一个Java Library。对于Java项目,独立抽成一个库,更容易维护与扩展。

为什么把注解声明也单独抽成一个库,而不放到注解处理工具库中呢?

这样可以不用将注解处理器的相关代码大报道使用者的项目中去

注解器声明

其方法包含四个主要重写方法

  • init() :初始化调用
  • process():实际处理方法
  • getSupportedAnnotationTypes():返回当前注解器处理注解的类型
  • getSupportedSourceVersion():指定你使用的java版本

在注解处理过程中,会扫描所有的java源文件,查询注解,在java源代码中,每一部分都代表一个特定类型的Element,比如:

1
2
3
4
5
6
7
8
9
10
package com.jennifer.andy.aptdemo.domain;//PackageElement
class Person {//TypeElement
private String where;//VariableElement

public void doSomething() { }//ExecutableElement

public void run() {//ExecutableElement
int runTime;//VariableElement
}
}

注解处理器是运行在它自己的虚拟机JVM中,javac启动一个完整Java虚拟机来运行注解处理器。

最终是通过自定义注解器在编译时期生成加载注解后的类,在process方法中具体做了执行,编译了程序模版信息进行添加。

自定义注解

如果是单一属性,可以使用value字段

1
2
3
4
5
6
7
8
9
10
11
@interface MyAnno1 {
//格式:类型名 属性名()
String value();
}
@MyAnno1("kang")

@interface MyAnno2 {
//格式:类型名 属性名()
String name();
}
@MyAnno2(name = "kang")

如果不是value字段的话,需要(指定属性 = 值)

注解中只允许八中基本数据类型、字符串、类类型,注解类型,枚举类型及其一维数组