一小時(shí)搞明白自定義注解(Annotation)
原創(chuàng)微信公眾號(hào)郭霖 WeChat ID: guolin_blog
什么是注解
Annotation(注解)就是Java提供了一種元程序中的元素關(guān)聯(lián)任何信息和著任何元數(shù)據(jù)(metadata)的途徑和方法。Annotion(注解)是一個(gè)接口为牍,程序可以通過(guò)反射來(lái)獲取指定程序元素的 Annotion對(duì)象野宜,然后通過(guò) Annotion對(duì)象 來(lái)獲取注解里面的元數(shù)據(jù)闷袒。
Annotation(注解)是JDK5.0及以后版本引入的犀斋。它可以用于創(chuàng)建文檔奏篙,跟蹤代碼中的依賴性润努,甚至執(zhí)行基本編譯時(shí)檢查关斜。從某些方面看,annotation 就像修飾符一樣被使用铺浇,并應(yīng)用于包痢畜、類 型、構(gòu)造方法鳍侣、方法丁稀、成員變量、參數(shù)倚聚、本地變量的聲明中线衫。這些信息被存儲(chǔ)在 Annotation 的“name=value”結(jié)構(gòu)對(duì)中。
什么是metadata元數(shù)據(jù)
元數(shù)據(jù)從metadata一詞譯來(lái)惑折,就是“關(guān)于數(shù)據(jù)的數(shù)據(jù)”的意思授账。
元數(shù)據(jù)的功能作用有很多枯跑,比如:你可能用過(guò) Javadoc 的注釋自動(dòng)生成文檔。這就是元數(shù)據(jù)功能的一種白热×仓總的來(lái)說(shuō),元數(shù)據(jù)可以用來(lái)創(chuàng)建文檔屋确,跟蹤代碼的依賴性纳击,執(zhí)行編譯時(shí)格式檢查,代替已有的配置文件乍恐。如果要對(duì)于元數(shù)據(jù)的作用進(jìn)行分類评疗,目前還沒(méi)有明確的定義,不過(guò)我們可以根據(jù)它所起的作用茵烈,大致可分為三類:
1. 編寫(xiě)文檔:通過(guò)代碼里標(biāo)識(shí)的元數(shù)據(jù)生成文檔
2. 代碼分析:通過(guò)代碼里標(biāo)識(shí)的元數(shù)據(jù)對(duì)代碼進(jìn)行分析
3. 編譯檢查:通過(guò)代碼里標(biāo)識(shí)的元數(shù)據(jù)讓編譯器能實(shí)現(xiàn)基本的編譯檢查
Annotation和Annotation類型
Annotation使用了在 java5.0 所帶來(lái)的新語(yǔ)法百匆,它的行為十分類似public、final這樣的修飾符呜投。每個(gè) Annotation 具有 一個(gè)名字 和 成員個(gè)數(shù)>=0加匈。每個(gè) Annotation 的成員具有被稱為 name=value 對(duì)的名字和值(就像 javabean 一樣),name=value 裝載了 Annotation 的信息仑荐。
Annotation類型定義了 Annotation 的名字雕拼、類型、成員默認(rèn)值粘招。一個(gè) Annotation類型 可以說(shuō)是一個(gè)特殊的java接口啥寇,它的成員變量是受限制的,而聲明 Annotation類型 時(shí)需要使用新語(yǔ)法洒扎。當(dāng)我們通過(guò) java反射api 訪問(wèn) Annotation 時(shí)辑甜,返回值將是一個(gè)實(shí)現(xiàn)了該 ?annotation類型 接口的對(duì)象,通過(guò)訪問(wèn)這個(gè)對(duì)象我們能方便的訪問(wèn)到其 Annotation 成員袍冷。后面的章節(jié)將提到在 java5.0 的 java.lang 包里包含的3個(gè)標(biāo)準(zhǔn) Annotation類型磷醋。
注解的分類
根據(jù)注解的參數(shù)個(gè)數(shù)分類:
1.標(biāo)記注解,一個(gè)沒(méi)有成員的Annotation類型被稱為標(biāo)記注解胡诗,這種類型僅僅使用自身的存在與否來(lái)為我們提供信息邓线,比如常見(jiàn)的@Override
2.單值注解
3.完整注解
根據(jù)注解使用的方法和用途分類:
1.JDK內(nèi)置系統(tǒng)注解
2.元注解
3.自定義注解
元注解
元注解的作用就是負(fù)責(zé)注解其他注解,Java?5.0定義了4個(gè)meta-annotation類型煌恢,用來(lái)提供對(duì)愛(ài)他的 annotation 類型做說(shuō)明骇陈。
java.lang.annotation
@Target
@Retention
@Document
@Inhrited
@Target
修飾的對(duì)象范圍:packages、types(類瑰抵、接口缩歪、枚舉、Annotation類型)谍憔、類型成員(方法匪蝙、構(gòu)造方法、成員變量习贫、枚舉值)逛球、方法參數(shù)和本地變量(如循環(huán)變量、catch參數(shù))苫昌。
作用:用于描述注解的使用范圍颤绕。
ElementType取值:
1. CONSTRUCTOR:用于描述構(gòu)造器
2. FIELD:用于描述域
3. LOCAL_VARIABLE:用于描述局部變量
4. METHOD:用于描述方法
5. PACKAGE:用于描述包
6. PARAMETER:用于描述參數(shù)
7. TYPE:用于描述類、接口(包括注解類型) 或enum聲明
例如祟身,Name 可以注解類的成員變量:
@Target(ElementType.FIELD)@Documented
public@interfaceName{Stringvalue() default "";}
Person 可以注解類奥务、接口(包括注解類型)、或者enum聲明:
@Target(ElementType.TYPE)
public@interfacePerson{
Stringvalue() default "";}
@Retention
定義了該 Annotation 被保留的時(shí)間長(zhǎng)短:某些 Annotation 僅出現(xiàn)在源代碼中袜硫,而被編譯器丟棄氯葬;而另一些卻被編譯在class文件中;編譯在class文件中的Annotation可能會(huì)被虛擬機(jī)忽略婉陷,而另一些在 class 被裝載時(shí)將被讀戎愠啤(請(qǐng)注意并不影響 class 的執(zhí)行,因?yàn)?Annotation 與 class 在使用上是被分離的)秽澳。使用這個(gè) meta-Annotation 可以對(duì) ?Annotation 的“生命周期”限制闯睹。
作用:表示需要在什么級(jí)別保存該注釋信息,用于描述注解的生命周期(即:被描述的注解在什么范圍內(nèi)有效)
RetentionPoicy取值:
SOURCE:在源文件中有效(即源文件保留)
CLASS:在class文件中有效(即class保留)
RUNTIME:在運(yùn)行時(shí)有效(即運(yùn)行時(shí)保留)
例如:Name 注解的 RetentionPolicy 的值為RUNTIME担神,這樣注解處理器可以通過(guò)反射楼吃,獲取到該注解的屬性,從而做一些運(yùn)行時(shí)的邏輯處理妄讯。
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public@interfaceName{
Stringvalue() default "";}
@Document
用于描述其它類型的 annotation 應(yīng)該被作為被標(biāo)注的程序成員的公共API孩锡,因此可以被例如 javadoc 此類的工具文檔化。Documented 是一個(gè)標(biāo)記注解捞挥,沒(méi)有成員浮创。
作用:表示需要在什么級(jí)別保存該注釋信息,用于描述注解的生命周期(即:被描述的注解在什么范圍內(nèi)有效)砌函。
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public@interfaceName{
Stringvalue() default "";}
@Inhrited
是一個(gè)標(biāo)記注解斩披,@Inherited闡述了某個(gè)被標(biāo)注的類型是被繼承的。如果一個(gè)使用了@Inherited 修飾的 annotation類型 被用于一個(gè) class讹俊,則這個(gè) annotation 將被用于該class的子類垦沉。
@Inheritedannotation類型 是被標(biāo)注過(guò)的class的子類所繼承。類并不從它所實(shí)現(xiàn)的接口繼承annotation仍劈,方法并不從它所重載的方法繼承annotation
當(dāng)@Inheritedannotation類型 標(biāo)注的 annotation 的 Retention 是RetentionPolicy.RUNTIME厕倍,則反射API增強(qiáng)了這種繼承性。如果我們使用java.lang.reflect 去查詢一個(gè) @Inherited annotation類型 的 annotation 時(shí)贩疙,反射代碼檢查將展開(kāi)工作:檢查class和其父類讹弯,直到發(fā)現(xiàn)指定的 annotation 類型被發(fā)現(xiàn)况既,或者到達(dá)類繼承結(jié)構(gòu)的頂層。
自定義注解
使用@interface自定義注解時(shí)组民,自動(dòng)繼承了 java.lang.annotation.Annotation 接口棒仍,由編譯程序自動(dòng)完成其他細(xì)節(jié)。在定義注解時(shí)臭胜,不能繼承其他的注解或接口莫其。@interface 用來(lái)聲明一個(gè)注解,其中的每一個(gè)方法實(shí)際上是聲明了一個(gè)配置參數(shù)耸三。方法的名稱就是參數(shù)的名稱乱陡,返回值類型就是參數(shù)的類型(返回值類型只能是 基本類型、Class仪壮、String憨颠、enum)【Σ担可以通過(guò)default來(lái)聲明參數(shù)的默認(rèn)值烙心。
自定義注解格式:
public @interface 注解名{注解體}
所有基本數(shù)據(jù)類型(int,float,boolean,byte,double,char,long,short)
String類型
Class類型
enum類型
Annotation類型
以上所有類型的數(shù)組
Annotation類型里面的參數(shù)該怎么設(shè)定:
第一,只能用 public 或 默認(rèn)(default) 這兩個(gè)訪問(wèn)權(quán)修飾.例如,String value();這里把方法設(shè)為 defaul 默認(rèn)類型;
第二,參數(shù)成員只能用基本類型 byte,short,char,int,long,float,double,boolean 八種基本數(shù)據(jù)類型 和 String,Enum,Class,annotations 等數(shù)據(jù)類型,以及這一些類型的數(shù)組.例如,String value();這里的參數(shù)成員就為String;
第三,如果只有一個(gè)參數(shù)成員,最好把參數(shù)名稱設(shè)為"value",后加小括號(hào).例:下面的例子 Name 注解就只有一個(gè)參數(shù)成員乏沸。
Name姓名注解:
Gander性別注解:
Profile個(gè)人資料注解:
注解元素的默認(rèn)值
注解元素必須有確定的值淫茵,要么在定義注解的默認(rèn)值中指定,要么在使用注解時(shí)指定蹬跃,非基本類型的注解元素的值不可為null匙瘪。因此, 使用空字符串或0作為默認(rèn)值是一種常用的做法。這個(gè)約束使得處理器很難表現(xiàn)一個(gè)元素的存在或缺失的狀態(tài)蝶缀,因?yàn)槊總€(gè)注解的聲明中丹喻,所有元素都存在,并且都具有相應(yīng)的值翁都,為了繞開(kāi)這個(gè)約束碍论,我們只能定義一些特殊的值,例如空字符串或者負(fù)數(shù)柄慰,一次表示某個(gè)元素不存在鳍悠,在定義注解時(shí),這已經(jīng)成為一個(gè)習(xí)慣用法坐搔。
注解處理器類庫(kù)(java.lang.reflect.AnnotatedElement)
注解元素Java使用 Annotation 接口來(lái)代表程序元素前面的注解藏研,該接口是所有 Annotation類型 的父接口。除此之外概行,Java 在 java.lang.reflect 包下新增了 AnnotatedElement 接口蠢挡,該接口代表程序中可以接受注解的程序元素,該接口主要有如下幾個(gè)實(shí)現(xiàn)類:
Class:類定義
Constructor:構(gòu)造器定義
Field:累的成員變量定義
Method:類的方法定義
Package:類的包定義
當(dāng)一個(gè) Annotation 被定義為運(yùn)行時(shí)Annotation后,改注解才是運(yùn)行時(shí)可見(jiàn)的业踏,當(dāng)class文件被裝載時(shí)被保存在class文件中的Annotation才會(huì)被虛擬機(jī)讀取禽炬。
AnnotatedElement接口提供了以下四個(gè)方法來(lái)訪問(wèn) Annotation 的信息:
方法1: T getAnnotation(Class annotationClass):返回改程序元素上存在的、指定類型的注解勤家,如果該類型注解不存在瞎抛,則返回null。
方法2:Annotation[] getAnnotations():返回該程序元素上存在的所有注解却紧。
方法3:boolean is AnnotationPresent(Class annotationClass):判斷該程序元素上是否包含指定類型的注解,存在則返回true胎撤,否則返回false.
方法4:Annotation[] getDeclaredAnnotations():返回直接存在于此元素上的所有注釋晓殊。與此接口中的其他方法不同,該方法將忽略繼承的注釋伤提。(如果沒(méi)有注釋直接存在于此元素上巫俺,則返回長(zhǎng)度為零的一個(gè)數(shù)組。)該方法的調(diào)用者可以隨意修改返回的數(shù)組肿男;這不會(huì)對(duì)其他調(diào)用者返回的數(shù)組產(chǎn)生任何影響介汹。
我們?yōu)榍懊娑x好的自定義注解寫(xiě)一個(gè)簡(jiǎn)單的處理器:
使用自定義注解:
運(yùn)行:
CustomUtils.getInfo(Person.class);
輸出:
完。舶沛。嘹承。。如庭。叹卷。。坪它。骤竹。。往毡。蒙揣。。开瞭。懒震。。惩阶。挎狸。。断楷。锨匆。
文章原創(chuàng)作者GuoLin 書(shū)籍推薦
郭林大神原創(chuàng)android 書(shū)籍:《第一行代碼 android》