深入理解Java:注解
注解(jie)作(zuo)用(yong):每當你創建描述符性質的類或者接口時,一旦其(qi)中(zhong)包含重復性的工作(zuo),就(jiu)可(ke)以考慮(lv)使用(yong)注解(jie)來簡化與自動化該過程。
Java提供(gong)了(le)四種元注解,專門負(fu)責新注解的創(chuang)建工作。
元注解
元(yuan)注(zhu)解(jie)的作(zuo)(zuo)用(yong)就是負(fu)責注(zhu)解(jie)其(qi)他注(zhu)解(jie)。Java5.0定(ding)義了(le)4個(ge)標準的meta-annotation類(lei)(lei)型,它們被用(yong)來(lai)提供(gong)對其(qi)它 annotation類(lei)(lei)型作(zuo)(zuo)說明。Java5.0定(ding)義的元(yuan)注(zhu)解(jie):
1.@Target,
2.@Retention,
3.@Documented,
4.@Inherited
這些(xie)類型(xing)和它們所支持的類在(zai)java.lang.annotation包中可以(yi)找(zhao)到。下面我們看(kan)一(yi)下每個元注解的作用和相應分參(can)數(shu)的使(shi)用說(shuo)明(ming)。
@Target
@Target說明了Annotation所修飾的(de)對象范圍:Annotation可被用于(yu) packages、types(類、接口、枚舉(ju)、Annotation類型(xing))、類型(xing)成員(方法、構造方法、成員變(bian)量(liang)、枚舉(ju)值(zhi))、方法參數和本地變(bian)量(liang)(如循環變(bian)量(liang)、catch參數)。在Annotation類型(xing)的(de)聲(sheng)明中使用了target可更加明晰其修飾的(de)目標(biao)。
作用:用于描述注解的使用范圍(即:被描述的注解可以用在什么地方)
取值(ElementType)有:
1.CONSTRUCTOR:用于描(miao)述構造器
2.FIELD:用于描述域
3.LOCAL_VARIABLE:用(yong)于描述局部變量
4.METHOD:用于描述方(fang)法
5.PACKAGE:用于描述(shu)包
6.PARAMETER:用于(yu)描述參數
7.TYPE:用于(yu)描述(shu)類、接口(kou)(包括(kuo)注(zhu)解(jie)類型) 或enum聲明
使(shi)用示(shi)例:
/***
*
* 實體注解接口
*/
@Target(value = {ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Entity {
/***
* 實體默(mo)認firstLevelCache屬性(xing)為false
* @return boolean
*/
boolean firstLevelCache() default false;
/***
* 實體默認secondLevelCache屬性為false
* @return boolean
*/
boolean secondLevelCache() default true;
/***
* 表名默(mo)認為空
* @return String
*/
String tableName() default "";
/***
* 默(mo)認以(yi)""分割(ge)注解(jie)
*/
String split() default "";
}
@Retention
@Retention定義了該Annotation被保留的時間長短:某些Annotation僅出現在源代碼中,而被編譯器丟棄;而另一些卻被編譯在class文件中;編譯在class文件中的Annotation可能會被虛擬機忽略,而另一些在class被裝載時將被讀取(請注意并不影響class的執行,因為Annotation與class在使用上是被分離的)。使用這個meta-Annotation可以對 Annotation的“生命周期”限制。
作用:表示需要在什么級別保存該注釋信息,用于描述注解的生命周期(即:被描述的注解在什么范圍內有效)
取值(RetentionPoicy)有:
1.SOURCE:在源文件(jian)中有(you)效(xiao)(即源文件(jian)保留)
2.CLASS:在class文件中有效(即(ji)class保留)
3.RUNTIME:在運行時有效(xiao)(即運行時保(bao)留)
使用(yong)示例:
/***
* 字段注解接口
*/
@Target(value = {ElementType.FIELD})//注解可以(yi)被添加在(zai)屬性上
@Retention(value = RetentionPolicy.RUNTIME)//注(zhu)解(jie)保存在JVM運行時(shi)刻,能夠在運行時(shi)刻通過反射API來獲取到(dao)注(zhu)解(jie)的(de)信息
public @interface Column {
String name();//注解(jie)的name屬(shu)性
}
Column注(zhu)解(jie)的(de)(de)的(de)(de)RetentionPolicy的(de)(de)屬性值是RUTIME,這樣注(zhu)解(jie)處理器(qi)可(ke)以通(tong)過反射,獲取到該注(zhu)解(jie)的(de)(de)屬性值,從而去做一(yi)些運行時的(de)(de)邏輯處理
@Documented
@Documented用于描述其它類型的annotation應該被作為被標注的程序成員的公共API,因此可以被例如javadoc此類的工具文檔化。Documented是一個標記注解,沒有成員。
@Inherited
@Inherited 元注解是一個標記注解,@Inherited闡述了某個被標注的類型是被繼承的。如果一個使用了@Inherited修飾的annotation類型被用于一個class,則這個annotation將被用于該class的子類。
注(zhu)意(yi):@Inherited annotation類型(xing)是被標注(zhu)過的class的子類所繼承。類并(bing)不(bu)從它(ta)所實現(xian)的接口繼承annotation,方(fang)(fang)法(fa)并(bing)不(bu)從它(ta)所重載(zai)的方(fang)(fang)法(fa)繼承annotation。
當@Inherited annotation類(lei)(lei)型(xing)標注的(de)annotation的(de)Retention是(shi)RetentionPolicy.RUNTIME,則反(fan)射API增強(qiang)了這種繼承(cheng)性。如果我們(men)使用java.lang.reflect去查(cha)(cha)詢一(yi)個@Inherited annotation類(lei)(lei)型(xing)的(de)annotation時,反(fan)射代(dai)碼檢(jian)查(cha)(cha)將展(zhan)開工作:檢(jian)查(cha)(cha)class和其父(fu)類(lei)(lei),直到(dao)發(fa)現指定(ding)的(de)annotation類(lei)(lei)型(xing)被發(fa)現,或者(zhe)到(dao)達類(lei)(lei)繼承(cheng)結構(gou)的(de)頂層(ceng)。
自定義注解
使用(yong)@interface自定(ding)義注(zhu)(zhu)解(jie)時,自動(dong)繼(ji)承了(le)java.lang.annotation.Annotation接口,由編譯程序自動(dong)完成(cheng)其(qi)他細節。在定(ding)義注(zhu)(zhu)解(jie)時,不能繼(ji)承其(qi)他的(de)(de)(de)(de)注(zhu)(zhu)解(jie)或接口。@interface用(yong)來聲(sheng)明一(yi)個注(zhu)(zhu)解(jie),其(qi)中的(de)(de)(de)(de)每(mei)一(yi)個方法實(shi)際上是(shi)聲(sheng)明了(le)一(yi)個配置參數(shu)。方法的(de)(de)(de)(de)名稱就是(shi)參數(shu)的(de)(de)(de)(de)名稱,返回值(zhi)類型(xing)就是(shi)參數(shu)的(de)(de)(de)(de)類型(xing)(返回值(zhi)類型(xing)只能是(shi)基(ji)本類型(xing)、Class、String、enum)。可以通過default來聲(sheng)明參數(shu)的(de)(de)(de)(de)默認值(zhi)。
定義注解格式:
public @interface 注解(jie)名 {定義體}
注解參數的可支持數據類型:
1.所(suo)有基本數據類型(int,float,boolean,byte,double,char,long,short)
2.String類型(xing)
3.Class類型
4.enum類型
5.Annotation類(lei)型
6.以上所有(you)類型的數組
Annotation類型里(li)面的(de)參數該(gai)怎么設定:
第(di)一,只能用public或默認(ren)(default)這兩個訪問權修飾.例(li)如,String value();這里把方法設為defaul默認(ren)類(lei)型(xing);
第二,參數(shu)(shu)(shu)成員只能用基(ji)本(ben)類型byte,short,char,int,long,float,double,boolean八種基(ji)本(ben)數(shu)(shu)(shu)據類型和 String,Enum,Class,annotations等數(shu)(shu)(shu)據類型,以及這一些類型的(de)數(shu)(shu)(shu)組.例(li)如,String value();這里(li)的(de)參數(shu)(shu)(shu)成員就為String;
第三,如果只有(you)一個參(can)數(shu)成員,最好把(ba)參(can)數(shu)名稱設為"value",后加小括(kuo)號.例:下面的例子FruitName注解就只有(you)一個參(can)數(shu)成員。
簡單的自定義注(zhu)解和使用注(zhu)解實例:
示例1:
/***
*主鍵注解接(jie)口
*/
@Target(value = {ElementType.FIELD})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Id {
}
示例2:
/**屬性不需要被持久化注(zhu)解**/
@Target(value = {ElementType.FIELD})
@Retention(value = RetentionPolicy.RUNTIME)
@Documented
public @interface Transient {
}
注解元素的默認值:
注解元素必須有確定的值,要么在定義注解的默認值中指定,要么在使用注解時指定,非基本類型的注解元素的值不可為null。因此, 使用空字符串或0作為默認值是一種常用的做法。這個約束使得處理器很難表現一個元素的存在或缺失的狀態,因為每個注解的聲明中,所有元素都存在,并且都具有相應的值,為了繞開這個約束,我們只能定義一些特殊的值,例如空字符串或者負數,一次表示某個元素不存在,在定義注解時,這已經成為一個習慣用法。
定義了注(zhu)(zhu)解(jie),并在需要的(de)(de)時(shi)候(hou)給(gei)相關(guan)類,類屬性加上(shang)注(zhu)(zhu)解(jie)信(xin)(xin)息,如果沒有響(xiang)應的(de)(de)注(zhu)(zhu)解(jie)信(xin)(xin)息處(chu)理(li)流程,注(zhu)(zhu)解(jie)可以(yi)說是沒有實(shi)用價值。如何讓注(zhu)(zhu)解(jie)真真的(de)(de)發揮作(zuo)用,主要就(jiu)在于注(zhu)(zhu)解(jie)處(chu)理(li)方法,下一(yi)步我們將學習(xi)注(zhu)(zhu)解(jie)信(xin)(xin)息的(de)(de)獲取和(he)處(chu)理(li)!
如果沒有(you)用(yong)(yong)來讀(du)取(qu)注(zhu)解的(de)(de)方(fang)法和工作,那么注(zhu)解也就(jiu)不會比注(zhu)釋更(geng)有(you)用(yong)(yong)處(chu)了(le)。使用(yong)(yong)注(zhu)解的(de)(de)過(guo)程(cheng)中,很(hen)重要的(de)(de)一部分(fen)就(jiu)是創建于使用(yong)(yong)注(zhu)解處(chu)理器。Java SE5擴展(zhan)了(le)反(fan)射機制的(de)(de)API,以幫助程(cheng)序員快速的(de)(de)構造自(zi)定義(yi)注(zhu)解處(chu)理器。
注解處理器類庫(java.lang.reflect.AnnotatedElement):
Java使用Annotation接(jie)(jie)口來代表(biao)程(cheng)序(xu)元素前(qian)面的(de)注(zhu)解,該接(jie)(jie)口是所有(you)Annotation類型的(de)父接(jie)(jie)口。除(chu)此(ci)之外,Java在java.lang.reflect 包下新增了AnnotatedElement接(jie)(jie)口,該接(jie)(jie)口代表(biao)程(cheng)序(xu)中可以(yi)接(jie)(jie)受注(zhu)解的(de)程(cheng)序(xu)元素,該接(jie)(jie)口主要有(you)如(ru)下幾個實現(xian)類:
Class:類(lei)定義(yi)
Constructor:構造器定(ding)義
Field:累的成員變量定義(yi)
Method:類的(de)方法定義
Package:類的包定義
java.lang.reflect 包(bao)下(xia)主要包(bao)含(han)一些實(shi)現反射功能的(de)工具類,實(shi)際上,java.lang.reflect 包(bao)所有提供的(de)反射API擴充了讀取(qu)運行(xing)時(shi)(shi)Annotation信息的(de)能力。當(dang)一個Annotation類型被(bei)定(ding)義為運行(xing)時(shi)(shi)的(de)Annotation后,該注解才能是運行(xing)時(shi)(shi)可(ke)見(jian),當(dang)class文件被(bei)裝載時(shi)(shi)被(bei)保存在class文件中的(de)Annotation才會被(bei)虛擬機讀取(qu)。
AnnotatedElement 接(jie)口是所有程序(xu)元素(Class、Method和(he)Constructor)的父接(jie)口,所以(yi)程序(xu)通過反射獲(huo)取了某個類(lei)的AnnotatedElement對(dui)象(xiang)之后,程序(xu)就可以(yi)調用該對(dui)象(xiang)的如下四個個方法來訪問Annotation信(xin)息:
方法1:<T extends Annotation> T getAnnotation(Class<T> annotationClass): 返(fan)回改程序元素上存(cun)在(zai)的、指定類(lei)型的注(zhu)解,如果該類(lei)型注(zhu)解不存(cun)在(zai),則返(fan)回null。
方法(fa)2:Annotation[] getAnnotations():返回該(gai)程序元素上存在的所有注解(jie)。
方法(fa)3:boolean is AnnotationPresent(Class<?extends Annotation> annotationClass):判斷該程序元素(su)上是否(fou)包含指定類(lei)型的注(zhu)解,存在(zai)則返(fan)回(hui)true,否(fou)則返(fan)回(hui)false.
方法(fa)4:Annotation[] getDeclaredAnnotations():返回(hui)直(zhi)接存在(zai)于此(ci)(ci)元(yuan)素上的(de)(de)(de)(de)所有注釋。與此(ci)(ci)接口中的(de)(de)(de)(de)其他(ta)方法(fa)不同,該(gai)方法(fa)將忽(hu)略繼(ji)承的(de)(de)(de)(de)注釋。(如果(guo)沒有注釋直(zhi)接存在(zai)于此(ci)(ci)元(yuan)素上,則返回(hui)長度為零(ling)的(de)(de)(de)(de)一個數(shu)組(zu)。)該(gai)方法(fa)的(de)(de)(de)(de)調(diao)用(yong)者可以隨意修改返回(hui)的(de)(de)(de)(de)數(shu)組(zu);這不會對其他(ta)調(diao)用(yong)者返回(hui)的(de)(de)(de)(de)數(shu)組(zu)產生任何影(ying)響(xiang)。
一個簡(jian)單的注解處理器:
/***********注解(jie)聲明***************/
/**
* 水果名稱注解
* @author peida
*
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitName {
String value() default "";
}
/**
* 水果顏色注解
* @author peida
*
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitColor {
/**
* 顏色枚舉
* @author peida
*
*/
public enum Color{ BULE,RED,GREEN};
/**
* 顏色屬性(xing)
* @return
*/
Color fruitColor() default Color.GREEN;
}
/**
* 水果(guo)供應者注解(jie)
* @author peida
*
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitProvider {
/**
* 供(gong)應商(shang)編號
* @return
*/
public int id() default -1;
/**
* 供應商名稱
* @return
*/
public String name() default "";
/**
* 供(gong)應商地址
* @return
*/
public String address() default "";
}
/***********注(zhu)解使用***************/
public class Apple {
@FruitName("Apple")
private String appleName;
@FruitColor(fruitColor=Color.RED)
private String appleColor;
@FruitProvider(id=1,name="陜西紅富士集團(tuan)",address="陜(shan)西省(sheng)西安市延安路89號紅富士大廈")
private String appleProvider;
public void setAppleColor(String appleColor) {
this.appleColor = appleColor;
}
public String getAppleColor() {
return appleColor;
}
public void setAppleName(String appleName) {
this.appleName = appleName;
}
public String getAppleName() {
return appleName;
}
public void setAppleProvider(String appleProvider) {
this.appleProvider = appleProvider;
}
public String getAppleProvider() {
return appleProvider;
}
public void displayName(){
System.out.println("水果的(de)名字是:蘋果");
}
}
/***********注解處理器***************/
public class FruitInfoUtil {
public static void getFruitInfo(Class<?> clazz){
String strFruitName=" 水果(guo)名稱(cheng):";
String strFruitColor=" 水果顏色:";
String strFruitProvicer="供應商(shang)信息:";
Field[] fields = clazz.getDeclaredFields();
for(Field field :fields){
if(field.isAnnotationPresent(FruitName.class)){
FruitName fruitName = (FruitName) field.getAnnotation(FruitName.class);
strFruitName=strFruitName+fruitName.value();
System.out.println(strFruitName);
}
else if(field.isAnnotationPresent(FruitColor.class)){
FruitColor fruitColor= (FruitColor) field.getAnnotation(FruitColor.class);
strFruitColor=strFruitColor+fruitColor.fruitColor().toString();
System.out.println(strFruitColor);
}
else if(field.isAnnotationPresent(FruitProvider.class)){
FruitProvider fruitProvider= (FruitProvider) field.getAnnotation(FruitProvider.class);
strFruitProvicer=" 供應商(shang)編(bian)號:"+fruitProvider.id()+" 供應商名稱:"+fruitProvider.name()+" 供應(ying)商地址:"+fruitProvider.address();
System.out.println(strFruitProvicer);
}
}
}
}
/***********輸出(chu)結(jie)果***************/
public class FruitRun {
/**
* @param args
*/
public static void main(String[] args) {
FruitInfoUtil.getFruitInfo(Apple.class);
}
}
====================================
水(shui)果(guo)名稱:Apple
水(shui)果(guo)顏色(se):RED
供應商編(bian)號:1 供應商名稱:陜西紅富士集團 供應商地址:陜西省西安市延安路89號紅富士大廈
Java注(zhu)(zhu)解(jie)的基礎(chu)知(zhi)識點(見下(xia)面導(dao)圖(tu))基本都過(guo)了一(yi)(yi)遍,下(xia)一(yi)(yi)篇(pian)我(wo)們通過(guo)設計一(yi)(yi)個基于注(zhu)(zhu)解(jie)的簡單的ORM框(kuang)架,來(lai)綜合應用和進一(yi)(yi)步(bu)加(jia)深對注(zhu)(zhu)解(jie)的各個知(zhi)識點的理(li)解(jie)和運用。

參考:
深入理解Java:注解(Annotation)--注解處理器
