怎樣理解Java的注解并運(yùn)用注解編程?
一、什么是注解
注解也叫元數(shù)據(jù),例如我們常見(jiàn)的@Override和@Deprecated,注解是JDK1.5版本開(kāi)始引入的一個(gè)特性,
用于對(duì)代碼進(jìn)行說(shuō)明,可以對(duì)包、類(lèi)、接口、字段、方法參數(shù)、局部變量等進(jìn)行注解。
一般常用的注解可以分為三類(lèi):
1、Java自帶的標(biāo)準(zhǔn)注解,包括@Override(標(biāo)明重寫(xiě)某個(gè)方法)、@Deprecated(標(biāo)明某個(gè)類(lèi)或方法過(guò)時(shí))和@SuppressWarnings(標(biāo)明要忽略的警告),使用這些注解后編譯器就會(huì)進(jìn)行檢查。
2、類(lèi)為元注解,元注解是用于定義注解的注解,包括@Retention(標(biāo)明注解被保留的階段)、@Target(標(biāo)明注解使用的范圍)、@Inherited(標(biāo)明注解可繼承)、@Documented(標(biāo)明是否生成javadoc文檔)
3、類(lèi)為自定義注解,可以根據(jù)自己的需求定義注解
二、注解的用途
在看注解的用途之前,有必要簡(jiǎn)單的介紹下XML和注解區(qū)別,
注解:是一種分散式的元數(shù)據(jù),與源代碼緊綁定。
xml:是一種集中式的元數(shù)據(jù),與源代碼無(wú)綁定
當(dāng)然網(wǎng)上存在各種XML與注解的辯論哪個(gè)更好,這里不作評(píng)論和介紹,主要介紹一下注解的主要用途:
生成文檔,通過(guò)代碼里標(biāo)識(shí)的元數(shù)據(jù)生成javadoc文檔。
編譯檢查,通過(guò)代碼里標(biāo)識(shí)的元數(shù)據(jù)讓編譯器在編譯期間進(jìn)行檢查驗(yàn)證。
編譯時(shí)動(dòng)態(tài)處理,編譯時(shí)通過(guò)代碼里標(biāo)識(shí)的元數(shù)據(jù)動(dòng)態(tài)處理,例如動(dòng)態(tài)生成代碼。
運(yùn)行時(shí)動(dòng)態(tài)處理,運(yùn)行時(shí)通過(guò)代碼里標(biāo)識(shí)的元數(shù)據(jù)動(dòng)態(tài)處理,例如使用反射注入實(shí)例
三、注解的實(shí)現(xiàn)原理
實(shí)現(xiàn)注解三要素:
1、注解聲明
2、使用注解的元素
3、操作注解使其起作用(注解處理器)
注解聲明
首先我們讓看一下java中的元注解(也就是上面提到的注解的注解),總共有4個(gè)如下:
@Target,@Retention,@Documented,@Inherited這4個(gè)元注解都是在jdk的java.lang.annotation包下面,
@Target:Target說(shuō)明的是Annotation所修飾的對(duì)象范圍。@Retention:
定義了該Annotation被保留的時(shí)間長(zhǎng)短:某些Annotation僅出現(xiàn)在源代碼中,而被編譯器丟棄;而另一些卻被編譯在class文件中;編譯在class文件中的Annotation可能會(huì)被虛擬機(jī)忽略,
而另一些在class被裝載時(shí)將被讀取(請(qǐng)注意并不影響class的執(zhí)行,因?yàn)锳nnotation與class在使用上是被分離的)。
使用這個(gè)元注解可以對(duì) Annotation的“生命周期”限制。@Documented:@Documented用于描述其它類(lèi)型的annotation應(yīng)該被作為被標(biāo)注的程序成員的公共API,因此可以被例如javadoc此類(lèi)的工具文檔化。Documented是一個(gè)標(biāo)記注解,沒(méi)有成員
@Inherited:@Inherited 元注解是一個(gè)標(biāo)記注解,@Inherited闡述了某個(gè)被標(biāo)注的類(lèi)型是被繼承的。如果一個(gè)使用了@Inherited修飾的annotation類(lèi)型被用于一個(gè)class,
則這個(gè)annotation將被用于該class的子類(lèi)。
注意:@Inherited annotation類(lèi)型是被標(biāo)注過(guò)的class的子類(lèi)所繼承。類(lèi)并不從它所實(shí)現(xiàn)的接口繼承annotation,方法并不從它所重載的方法繼承annotation。
當(dāng)@Inherited annotation類(lèi)型標(biāo)注的annotation的Retention是RetentionPolicy.RUNTIME,則反射API增強(qiáng)了這種繼承性。如果我們使用java.lang.reflect去查詢一個(gè)@Inherited annotation類(lèi)型的annotation時(shí),
反射代碼檢查將展開(kāi)工作:檢查class和其父類(lèi),直到發(fā)現(xiàn)指定的annotation類(lèi)型被發(fā)現(xiàn),或者到達(dá)類(lèi)繼承結(jié)構(gòu)的頂層。
java.lang.reflect.AnnotatedElement 接口是所有程序元素(Class、Method和Constructor)的父接口,所以程序通過(guò)反射獲取了某個(gè)類(lèi)的AnnotatedElement對(duì)象之后,程序就可以調(diào)用該對(duì)象的如下四個(gè)個(gè)方法來(lái)訪問(wèn)Annotation信息:
方法1:<T extends Annotation> T getAnnotation(Class<T> annotationClass): 返回改程序元素上存在的、指定類(lèi)型的注解,如果該類(lèi)型注解不存在,則返回null。
方法2:Annotation[] getAnnotations():返回該程序元素上存在的所有注解。
方法3:boolean is AnnotationPresent(Class<?extends Annotation> annotationClass):判斷該程序元素上是否