1.Annotation 的概念
An annotation is a form of metadata, that can be added to Java source code. Classes, methods, variables, parameters and packages may be annotated. Annotations have no direct effect on the operation of the code they annotate.
注解是一种可以添加到 Java 源代码的元数据.
类, 方法, 变量, 参数, 包都可以被注解.
注解对注解的代码并没有直接的影响.
注解仅仅是个标记. 注解之所以起作用是对其解析后做了相应的处理
2.Annotation 分类
标准 Annotation
标准 Annotation 是指 Java 内置的三个 Annnotaion:
@Override: 用于修饰此方法覆盖了父类的方法.
@Deprecated: 用于修饰已经过时的方法.
@SuppressWarnnings: 用于通知 java 编译器禁止特定的编译警告.
元 Annotation(注解的注解)
元 Annotation 是用来定义 Annotation 的 Annotation
元 Annotation 可以定义 Annotation 的作用范围, 使用在什么元素上等
元注解共有四种 @Retention, @Target, @Inherited, @Documented
自定义 Annotation
3. 元 Annotation
@Retention: 注在其他的注解 A 上, 用来说明 A 的保留范围, 可选值 SOURCE(源码时),CLASS(编译时),RUNTIME(运行时), 默认为 CLASS
SOURCE:A 只保留在源码中, A 会被编译期忽略.(源码可用)
CLASS:A 会通过编译保存在 CLASS 文件中, 但会被 JVM 在运行时忽略, 运行时不可见.(源码 + CLASS 可用)
RUNTIME:A 会被 JVM 获取, 并在运行时通过反射获取.(源码 + CLASS + 运行时均可用)
@Target: 注在其他的注解 A 上, 用来限制 A 可用修饰那些程序元素. 未标注 Target 表示无限制, 可修饰所有元素.
ANNOTATION_TYPE: A 可以应用到其他注解上
CONSTRUCTOR: A 可以使用到构造器上
FIELD: A 可以使用到域或属性上
LOCAL_VARIABLE: A 可以使用到局部变量上.
METHOD: A 可以使用到方法上.
PACKAGE: A 可以使用到包声明上.
PARAMETER: A 可以使用到方法的参数上
TYPE: A 可以使用到类, 接口(包括注解), 或枚举的声明上
@Inherited: 默认情况下, 父类的注解不会被子类继承.
Inherited 注在其他的注解 A 上.
只有当 A 是注解在类 Class 上面, Inherited 才会起作用, 其他任何情况下无效果.
当 A 注解在类 C 上面, 则 C 的所有子孙类, 都会继承应用 A 注解;
@Documented: 注在其他的注解 A 上, A 将会作为 Javadoc 产生的文档中的内容. 注解都默认不会成为成为文档中的内容.
4. 自定义 Annotation
创建自定义 Annotation 流程
public @interface 自定义注解名称
public @interface CustomAnnotation{***}
设置自定义 Annotation 的保留范围和目标, Retention 和 Target 是最重要的两个元 Anitation.
- @Retention( RetentionPolicy.RUNTIME )
- @Target( ElementType.TYPE )
- public @interface CustomAnnotation{***}
设置自定义 Annotation 的注解参数(注解成员)
注解参数支持的数据类型
所有基本数据类型(int,float,boolean,byte,double,char,long,short)
String 类型
Class 类型
enum 类型
Annotation 类型
以上所有类型的一维数组
注解参数声明方式
- @Retention( RetentionPolicy.RUNTIME )
- @Target( ElementType.TYPE )
- public @interface CustomAnnotation{
- // 注解参数类型可以是 1-6 中任一种, 包括枚举
- public enum Skill{JAVA,ANDROID,IOS}
- Skill mySkill() default Skill.ANDROID;
- String attr1();
- // 可以使用 default 设置默认值
- int attr2() default 100;
- // 修饰符只能用 public
- public boolean attr3() default false;
- }
- @Retention( RetentionPolicy.RUNTIME )
- @Target( ElementType.TYPE )
- public @interface CustomAnnotation{
- // 只有一个注解参数, 使用 value()
- String value();
- }
自定义 Annotation 的参数类型必须满足上一条 1 到 6 中的范围.
自定义 Annotation 的参数访问方法只能是 public, 或不写.
自定义 Annotation 的参数可以加 default 设置默认值.
自定义 Annotation 若只有 1 个参数, 使用 value().
自定义 Annotation 的注解参数的默认值
注解元素必须有确定的值, 要么在定义注解的默认值中指定, 要么在使用注解时指定, 非基本类型的注解元素的值不可为 null. 因此, 使用空字符串或 0 作为默认值是一种常用的做法. 这个约束使得处理器很难表现一个元素的存在或缺失的状态, 因为每个注解的声明中, 所有元素都存在, 并且都具有相应的值, 为了绕开这个约束, 我们只能定义一些特殊的值, 例如空字符串或者负数, 一次表示某个元素不存在, 在定义注解时, 这已经成为一个习惯用法.
示例:
- @Target(ElementType.FIELD)
- @Retention(RetentionPolicy.RUNTIME)
- public @interface AnotherAnnotation{
- String author() default "";
- int age() default -1;
- }
使用刚刚创建的自定义注解
- @CustomAnnotation(attr1 = "属性 1", attr2 = 90, attr3 = true)
- public class AnnotationTestClass{
- ***
- }
5.Annotation 解析
运行时 Annotation 解析
运行时 Annotation 指 @Retention 为 RUNTIME 的 Annotation
- Class,Method,Field 中都有以下 3 个方法可以调用
- public <T extends Annotation> T getAnnotation(Class<T> annotationClass) 按照传入的参数获取指定类型的注解. 返回 null 说明当前元素不带有此注解.
- public final boolean isAnnotationPresent(Class<? extends Annotation> annotationType) 检查传入的注解是否存在于当前元素.
- public Annotation[] getAnnotations() 返回该元素的所有注解, 包括没有显式定义该元素上的注解.
- 运行时 Annotation 解析示例
public void testCustomAnnotation() { try { Class cls = Class.forName("com.jet.annotation.AnnotationTestClass"); CustomAnnotation customAnnotation = (CustomAnnotation)cls.getAnnotation(CustomAnnotation.class); System.out.println("customAnnotation mySkill:" + cus.mySkill()); System.out.println("customAnnotation attr1:" + cus.attr1()); System.out.println("customAnnotation attr2:" + cus.attr2()); } catch (ClassNotFoundException e) { e.printStackTrace(); } }
编译时 Annotation 解析
编译时 Annotation 指 @Retention 为 CLASS 的 Annotation, 甴编译器自动解析
6. 编译时 Annotation 解析
编译时 Annotation 解析 相对复杂, 下面单独进行分析
首先申明: 下面内容仅仅讨论 编译时 Annotation 的解析
编译时 Annotation 的解析, 是由 Annotation Processor 完成
Annotation Processor(注解处理器)
注解处理器是一个在 javac 中的, 用来在编译时扫描和处理注解的工具
我们可以为特定的注解, 注册自定义的注解处理器
在编译期间, JVM 会自动运行注册过的注解处理器
一个注解的 Annotation Processor, 以 Java 代码 (或者编译过的 class) 为输入, 生成. java 文件作为输出. 这意味着我们可以生成新的 Java 代码! 这些生成的 Java 代码是在生成的. java 文件中, 新生成的. java 文件会和普通的手动编写的 Java 源代码一样被 javac 编译
每一个注解处理器都是继承于 AbstractProcessor, 需要关注的有以下 4 个方法
- public abstract class AbstractProcessor implements Processor {
- // 对一些工具进行初始化
- public synchronized void init(ProcessingEnvironment processingEnv)
- // 你在这里定义你的注解处理器注册到哪些注解上, 必须指定;
- // 它的返回值是一个字符串的集合, 包含本处理器想要处理的注解类型的合法全称
- public Set<String> getSupportedAnnotationTypes()
- // 指定该注解处理器使用的 JAVA 版本, 通常返回 SourceVersion.latestSupported()
- public SourceVersion getSupportedSourceVersion()
- // 真正生成 java 代码的地方
- //annotations: 请求处理的注解类型集合
- //roundEnv: 可以让你查询出包含特定注解的被注解元素, 相当于 "有关全局源码的上下文环境"
- // 如果返回 true, 则这些注解已声明并且不要求后续 Processor 处理它们;
- // 如果返回 false, 则这些注解未声明并且可能要求后续 Processor 处理它们
- public abstract boolean process(Set<? extends TypeElement> annotations,RoundEnvironment roundEnv)
- }
自定义注解处理器, 就是继承 AbstractProcessor 并重写上述 4 个方法
关于编译时 Annotation 解析, 这里推荐一篇文章[Android] 注解框架(三)-- 编译时注解, 手写 ButterKnife, 按照文章上面流程敲一遍代码, 相信可以对自定义注解的创建及解析有一个深入的了解!
来源: http://www.jianshu.com/p/f7c782381548