學(xué)習(xí)注解原理的理由
越來(lái)越多的Android庫(kù)中使用的注解,比如butterknife,EventBus3,okHttp里面也是使用了注解,減少了重復(fù)代碼的編寫(xiě),極大的方便我們快速開(kāi)發(fā),那么了解其內(nèi)部的工作原理極其重要,而且如果我們不知道其中的原理,我們?cè)谑褂眠^(guò)程中遇到的相關(guān)問(wèn)題就會(huì)一頭霧水,難以解決,所以我決定寫(xiě)一個(gè)注解Annotation的系列文章,窺探注解之秘。
注解(Annotation)是什么?
Annotation是元數(shù)據(jù)的一種形式,向外提供程序的信息,但它本身并不是這個(gè)程序的一部分,它可以被添加到包,類,方法,變量中,并且可以在某個(gè)生命周期中(java源碼中,編譯期,Runtime)被反射獲取。Annotation并不是直接影響它所注解的代碼 。
簡(jiǎn)單來(lái)說(shuō),注解(Annotation) 為我們?cè)诖a中添加信息提供了一種形式化的方法,是我們可以在稍后某個(gè)時(shí)刻方便地使用這些數(shù)據(jù)(通過(guò) 解析注解 來(lái)使用這些數(shù)據(jù))
注解(Annotation)用來(lái)做什么?
1.給編譯器提供信息--例如提供給編譯器探測(cè)錯(cuò)誤和壓制警告等等
2.編譯期生成代碼
3.運(yùn)行期(Runtime)處理注解
自定義注解
新建一個(gè)java library的module,新建一個(gè)class
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface SourceAnnotation {
String value() default "SourceAnnotation";
}
先講一下元注解的概念,用來(lái)注解注解類的注解就是元注解,java提供了五種元注解,分別是@Documented,@Inherited, @Repeatable, @Target, @Retention
@Documented 它代表著此注解的元素會(huì)被javadoc工具提取成文檔
@Inherited 允許子類繼承父類中的注解
@Repeatable Java SE8引入的注解,表示這個(gè)注解可以在同一處多次聲明
@Target 是用來(lái)描述該注解標(biāo)記哪一種類型在java源碼中,它的取值可為:
ElementType.ANNOTATION_TYPE 可以使用在注解類型上
ElementType.CONSTRUCTOR 可以使用在構(gòu)造方法上
ElementType.FIELD 可以使用在屬性(成員變量)上
ElementType.LOCAL_VARIABLE 可以使用在局部變量上
ElementType.METHOD 可以使用在方法上
ElementType.PACKAGE 可以使用在包聲明上
ElementType.PARAMETER 可以使用在方法參數(shù)上
ElementType.TYPE 可以使用在類中任何元素
@Retention代表這個(gè)注解的生命周期,可以存活到什么時(shí)期:
RetentionPolicy.SOURCE 存在在java源碼中
RetentionPolicy.CLASS 存活到編譯成Class中
RetentionPolicy.RUNTIME 存活到運(yùn)行時(shí)期
接下來(lái)重點(diǎn)理解一下這個(gè)Rentention,我們新建一個(gè)AnnotationClass的類,然后用上面定義的SourceAnnotation去注解它
@SourceAnnotation()
public class AnnotationClass {
}
編譯一下,
然后在build文件夾classes中查找到AnnotationClass.class文件查看
package com.example;
public class AnnotationClass {
public AnnotationClass() {
}
}
我們的注解@SourceAnnotation()已經(jīng)不存在了,這個(gè)就是RetentionPolicy.SOURCE的作用,使注解僅存在與java源碼中。
我接下來(lái)再定義一個(gè)注解,設(shè)置為RetentionPolicy.CLASS
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
public @interface ClassAnnotation {
public String value() default "ClassAnnotation";
}
同樣,我們來(lái)注解一下AnnotationClass
@ClassAnnotation()
public class AnnotationClass {
}
編譯,查找AnnotationClass.class文件
@ClassAnnotation
public class AnnotationClass {
public AnnotationClass() {
}
}
發(fā)現(xiàn)我們@ClassAnnotation的注解還是存在的。
最后,我們使用RetentionPolicy.RUNTIME,新建
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface RuntimeAnnotation {
String value() default "RuntimeAnnotation";
}
注解AnnotationClass,編譯,猜想一下,是不是一樣存在?
@RuntimeAnnotation
public class AnnotationClass {
public AnnotationClass() {
}
}
是的,同在編譯成Class文件中可以查找到,那么RetentionPolicy.Runtime和RetentionPolicy.CLASS區(qū)別又在哪里呢?這涉及到Annotation的使用,我們上面提到,Annotation信息的獲取是通過(guò)反射獲取的,我們可以通過(guò)Class中g(shù)etAnnotation的方法來(lái)獲取接下來(lái),我們來(lái)嘗試獲取AnnationClass類中的注解信息。
注解信息的獲取
我們?cè)贏nnotationTest中,寫(xiě)一個(gè)Main方法,作為程序的入口,編寫(xiě)一下代碼
public class AnnotationTest {
public static void main(String[] args){
Class annotationClass = AnnotationClass.class;
RuntimeAnnotation annotation = (RuntimeAnnotation) annotationClass.getAnnotation(RuntimeAnnotation.class);
String value = annotation.value();
System.out.println("value:"+value);
}
}
我們上面定義了RuntimeAnnotation注解的默認(rèn)的值是"RuntimeAnnotion",運(yùn)行一下
查看輸出
確實(shí)打印了RuntimeAnnotion的值,說(shuō)明運(yùn)行時(shí)期獲取到這個(gè)注解的值。接下來(lái),我們獲取一下ClassAnnotation這個(gè)注解的值,看能否獲取得到,修改AnnotationClass的注解為@ClassAnnotation
@ClassAnnotation()
public class AnnotationClass {
}
修改main方法為
public class AnnotationTest {
public static void main(String[] args){
Class annotationClass = AnnotationClass.class;
// RuntimeAnnotation annotation = (RuntimeAnnotation) annotationClass.getAnnotation(RuntimeAnnotation.class);
ClassAnnotation annotation = (ClassAnnotation) annotationClass.getAnnotation(ClassAnnotation.class);
String value = annotation.value();
System.out.println("value:"+value);
}
}
如果獲取得到話,應(yīng)該打印出的是默認(rèn)值"ClassAnnotation",我們運(yùn)行一下,查看輸出
我們發(fā)現(xiàn)報(bào)錯(cuò)了,而且報(bào)錯(cuò)的原因是在
String value = annotaion.value();
這一行報(bào)出空指針異常,也就是說(shuō)我們獲取ClassAnnotation這個(gè)注解不存在,我們之前看到過(guò),在編譯的class文件中,這個(gè)注解是存在的。所以這個(gè)就是RetentionPolicy.CLASS和RetentionPolicy.RUNTIME的區(qū)別,RetentionPolicy.CLASS的注解是不會(huì)存活到運(yùn)行時(shí)期的,在運(yùn)行時(shí)期要想通過(guò)反射獲得注解,那么你定義這個(gè)注解的時(shí)候需要使用RetentionPolicy.RUNTIME。
理解注解的基本使用之后,接下來(lái)我們將利用ART(Annotation Processing Tool)技術(shù)在編譯期生成代碼。