什么是元数据(metadata)元数据是由元数据翻译过来的,所谓元数据就是“关于数据的数据”,更通俗的说就是数据描述数据,描述性的关于数据和信息资源的信息。比如一个文本文件,有创建时间、创建者、文件大小等数据,可以理解为元数据。在java中,元数据以标签的形式存在于java代码中,它的存在并不影响程序代码的编译和执行。通常用于生成其他文件或在运行时获知运行代码的描述信息。java中的javadoc和注解都属于元数据。什么是注释?注解是从java5.0开始加入的,可以用来标记包、类、方法、变量等,比如我们常见的@Override,或者Android源码中的@hide、@systemApi、@privateApi等。Override,大多数人往往知道但不知道为什么。今天先说说Annotation背后的秘密,开始正文。元注解是定义注解的注解。Java为我们提供了用于定义注解的基本注解。在java.lang.annotation包中,我们可以看到目前有以下元注解:@Retention@Target@Inherited@Documented@interface接下来我们收集@Override注解来讲解五个基本注解的用法。@interface@interface是java中用于声明注解类的关键字。使用这个注解意味着java.lang.annotation.Annotation类会被自动继承,把这个过程交给编译器。因此如果我们要定义一个注解,我们只需要做下面的事情。以@Override注解为例public@interfaceOverride{}需要注意:在定义注解时,不能继承其他注解或接口@Retention@Retention:该注解用于定义注解保留策略,即定义的时候注释类存在(源代码阶段或编译或运行阶段之后)。该注解接受如下参数:RetentionPolicy.SOURCE、RetentionPolicy.CLASS、RetentionPolicy.RUNTIME,其具体用途和含义如下:看一下@Override注解的保留策略:@Retention(RetentionPolicy.SOURCE)public@interfaceOverride{}表示@Override注解只存在于源码阶段,javac在编译时去掉该注解。@Target这个注解用来定义注解的目标,也就是可以在什么地方使用注解,比如是用在方法上还是用在字段上,注解接受以下参数:以@Override举个例子,不难看出它的目标是一个方法:@Target(ElementType.METHOD)public@interfaceOverride{}到现在为止,通过@interface,@Retention,@Target可以完全定义一个注解。我们来看看@Override的完整定义:@Target(ElementType.METHOD)@Retention(RetentionPolicy.SOURCE)public@interfaceOverride{}@Inherited默认情况下,我们在父类上使用的自定义注解不会被子类继承。如果想让子类也继承父类的注解,即注解在子类中也会生效。自定义注解时需要设置@Inherited。一般情况下,这个注解用的比较少。@Documented该注解用于描述其他类型的注解,这些注解应该被javadoc记录并出现在apidoc中。例如@Target使用这个注解会出现在api描述中@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.ANNOTATION_TYPE)public@interfaceTarget{ElementType[]value();}借助@Interface,@Target,@Retention,@Inherited,@Documented这五个元注解,我们可以自定义注解,前三个注解是任何注解所必需的。自定义注解格式:public@interface注解名{定义体}定义体是方法的集合,每个方法实际上声明了一个配置参数。方法的名称作为配置参数的名称,方法的返回值类型为配置参数的类型。与普通方法不同,default关键字可以用来声明默认的配置参数Value。注意:这里只能使用public或者defaultdefault两种权限修饰符。配置参数的类型只能使用基本类型(byte,boolean,char,short,int,long,float,double)和String,Enum,Class,annotation对于只包含一个配置参数的注解,参数名推荐为设置在value中,即方法名是value。一旦设置了配置参数,其参数值就必须有一个确定的值,要么在使用注解时指定,要么在定义注解时,使用default来设置设置默认值。对于非基本类型的参数值,不能为空。和@Override一样,没有成员定义的注解称为标签注解。我们已经学习了如何在注解处理器之上定义注解。要使注解发挥实际作用,我们需要为注解编写相应的注解处理器。根据注解的特点,注解处理器可以分为运行时注解处理和编译时注解处理器。运行时处理器需要借助反射机制来实现,而编译时处理器需要借助APT来实现。无论是运行时注解处理器还是编译时注解处理器,主要的工作就是读取注解和处理具体的注解。从这个角度看,注解处理器还是很容易理解的。注解处理器(AnnotationProcessor)是javac的一个工具,用于在编译时扫描编译和处理注解(Annotation)。你可以自己定义注解和注解处理器来做一些事情。一种注解处理器,它将Java代码或(编译后的字节码)作为输入并生成文件(通常是java文件)。这些生成的java文件不能修改,会像他们手工编写的java代码一样被javac编译。看到这里再加上前面的理解,应该明白大概的流程了,就是把标注了注解的类和变量作为输入内容,通过注解处理器进行处理,生成你想要生成的java代码。Runtimeannotationprocessor(不推荐)熟悉java反射机制的同学一定对java.lang.reflect包非常熟悉。该包中的所有API都支持读取runtimeAnnotation的能力,即属性为@Retention(RetentionPolicy.RUNTIME)。java.lang.reflect中的AnnotatedElement接口是所有程序元素的(Class,Method)父接口。我们可以通过反射获取某个类的AnnotatedElement对象,然后使用该对象提供的方法获取注解信息的常用方法如下:写一个运行时注解处理器的本质就是通过反射获取注解信息,然后执行其他操作。编译运行时注解处理器就这么简单。运行时注解通常用于参数配置模块。编译时注解处理器不同于运行时注解处理器,编译时注解处理器(AnnotationProcessorTool)。APT用于在编译时扫描和处理注释信息。具体的annotationprocessor可以是java源文件或者编译后的最终class文件作为输入,然后输出其他文件,可以是.java文件也可以是.class文件,但通常我们输出的都是.java文件。(注意:它不是对源文件的修改)。如果输出的是一个.java文件,而这些.java文件是由javac和其他源代码文件一起编译出来的。您可能想知道,注解处理器在什么阶段进行干预?嗯,其实是在javac开始编译之前,这也是通常我们愿意输出.java文件的原因。Annotations最早在java5引入,主要包括apt和com.sum.mirror包中的相关镜像API。这时候apt和javac是独立的。从java6开始,注解处理器正式标准化,apt工具也直接集成到javac中。让我们回到如何编写编译时注解处理器的话题。编译一个编译时注解处理主要分为两步:1.继承AbstractProcessor,实现自己的注解处理器2.注册处理器,先打包成jar包。看下一个标准的注解处理器的格式:publicclassMyAnnotationProcessorextendsAbstractProcessor{@OverridepublicSet
