元注解
元注解的作用就是負責注解其他注解。Java5.0定義了4個標准的meta-annotation類型,它們被用來提供對其它 annotation類型作說明。Java5.0定義的元注解:
1.@Target,
2.@Retention,
3.@Documented,
4.@Inherited
這些類型和它們所支持的類在java.lang.annotation包中可以找到。下面我們看一下每個元注解的作用和相應分參數的使用說明。
@Target:
@Target說明了Annotation所修飾的對象范圍:Annotation可被用於 packages、types(類、接口、枚舉、Annotation類型)、類型成員(方法、構造方法、成員變量、枚舉值)、方法參數和本地變量(如循環變量、catch參數)。在Annotation類型的聲明中使用了target可更加明晰其修飾的目標
1.CONSTRUCTOR:用於描述構造器
2.FIELD:用於描述域即類成員變量
3.LOCAL_VARIABLE:用於描述局部變量
4.METHOD:用於描述方法
5.PACKAGE:用於描述包
6.PARAMETER:用於描述參數
7.TYPE:用於描述類、接口(包括注解類型) 或enum聲明
@Retention:
@Retention定義了該Annotation被保留的時間長短:某些Annotation僅出現在源代碼中,而被編譯器丟棄;而另一些卻被編譯在class文件中;編譯在class文件中的Annotation可能會被虛擬機忽略,而另一些在class被裝載時將被讀取(請注意並不影響class的執行,因為Annotation與class在使用上是被分離的)。使用這個meta-Annotation可以對 Annotation的“生命周期”限制。
作用:表示需要在什么級別保存該注釋信息,用於描述注解的生命周期(即:被描述的注解在什么范圍內有效)
取值(RetentionPoicy)有:
1.SOURCE:在源文件中有效(即源文件保留)
2.CLASS:在class文件中有效(即class保留)
3.RUNTIME:在運行時有效(即運行時保留)
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Column { public String name() default "fieldName"; public String setFuncName() default "setField"; public String getFuncName() default "getField"; public boolean defaultDBValue() default false; }
Column注解的的RetentionPolicy的屬性值是RUTIME,這樣注解處理器可以通過反射,獲取到該注解的屬性值,從而去做一些運行時的邏輯處理。
@Documented:
@Documented用於描述其它類型的annotation應該被作為被標注的程序成員的公共API,因此可以被例如javadoc此類的工具文檔化。Documented是一個標記注解,沒有成員。
@Inherited:
@Inherited 元注解是一個標記注解,@Inherited闡述了某個被標注的類型是被繼承的。如果一個使用了@Inherited修飾的annotation類型被用於一個class,則這個annotation將被用於該class的子類。
注意:@Inherited annotation類型是被標注過的class的子類所繼承。類並不從它所實現的接口繼承annotation,方法並不從它所重載的方法繼承annotation。
當@Inherited annotation類型標注的annotation的Retention是RetentionPolicy.RUNTIME,則反射API增強了這種繼承性。如果我們使用java.lang.reflect去查詢一個@Inherited annotation類型的annotation時,反射代碼檢查將展開工作:檢查class和其父類,直到發現指定的annotation類型被發現,或者到達類繼承結構的頂層.
實例代碼:
五、常見標准的Annotation
從java5版本開始,自帶了三種標准annontation類型,
(1)、Override
java.lang.Override 是一個marker annotation類型,它被用作標注方法。它說明了被標注的方法重載了父類的方法,起到了斷言的作用。如果我們使用了這種annotation在一個沒有覆蓋父類方法的方法時,java編譯器將以一個編譯錯誤來警示。
這個annotaton常常在我們試圖覆蓋父類方法而確又寫錯了方法名時加一個保障性的校驗過程。
(2)、Deprecated
Deprecated也是一種marker annotation。當一個類型或者類型成員使用@Deprecated修飾的話,編譯器將不鼓勵使用這個被標注的程序元素。所以使用這種修飾具有一定的 “延續性”:如果我們在代碼中通過繼承或者覆蓋的方式使用了這個過時的類型或者成員,雖然繼承或者覆蓋后的類型或者成員並不是被聲明為 @Deprecated,但編譯器仍然要報警。
注意:@Deprecated這個annotation類型和javadoc中的 @deprecated這個tag是有區別的:前者是java編譯器識別的,而后者是被javadoc工具所識別用來生成文檔(包含程序成員為什么已經過時、它應當如何被禁止或者替代的描述)。
(3)、SuppressWarnings
此注解能告訴Java編譯器關閉對類、方法及成員變量的警告。
有時編譯時會提出一些警告,對於這些警告有的隱藏着Bug,有的是無法避免的,對於某些不想看到的警告信息,可以通過這個注解來屏蔽。
SuppressWarning不是一個marker annotation。它有一個類型為String[]的成員,這個成員的值為被禁止的警告名。對於javac編譯器來講,被-Xlint選項有效的警告名也同樣對@SuppressWarings有效,同時編譯器忽略掉無法識別的警告名。
annotation語法允許在annotation名后跟括號,括號中是使用逗號分割的name=value對用於為annotation的成員賦值:
代碼:
- @SuppressWarnings(value={"unchecked","fallthrough"})
- public void lintTrap() { /* sloppy method body omitted */ }
注意:我們可以在下面的情況中縮寫annotation:當annotation只有單一成員,並成員命名為"value="。這時可以省去"value="。比如將上面的SuppressWarnings annotation進行縮寫:
代碼:
- @SuppressWarnings({"unchecked","fallthrough"})
如果SuppressWarnings所聲明的被禁止警告個數為一個時,可以省去大括號:
- @SuppressWarnings("unchecked")
在這個例子中SuppressWarnings annotation類型只定義了一個單一的成員,所以只有一個簡單的value={...}作為name=value對。又由於成員值是一個數組,故使用大括號來聲明數組值。
六、自定義注解
使用@interface自定義注解時,自動繼承了java.lang.annotation.Annotation接口,由編譯程序自動完成其他細節。在定義注解時,不能繼承其他的注解或接口。@interface用來聲明一個注解,其中的每一個方法實際上是聲明了一個配置參數。方法的名稱就是參數的名稱,返回值類型就是參數的類型(返回值類型只能是基本類型、Class、String、enum)。可以通過default來聲明參數的默認值。
定義注解格式:
public @interface 注解名 {定義體}
注解參數的可支持數據類型:
1.所有基本數據類型(int,float,boolean,byte,double,char,long,short)
2.String類型
3.Class類型
4.enum類型
5.Annotation類型
6.以上所有類型的數組
Annotation類型里面的參數該怎么設定:
第一,只能用public或默認(default)這兩個訪問權修飾.例如,String value();這里把方法設為defaul默認類型;
第二,參數成員只能用基本類型byte,short,char,int,long,float,double,boolean八種基本數據類型和 String,Enum,Class,annotations等數據類型,以及這一些類型的數組.例如,String value();這里的參數成員就為String;
第三,如果只有一個參數成員,最好把參數名稱設為"value",后加小括號.例:下面的例子FruitName注解就只有一個參數成員。
簡單的自定義注解和使用注解實例:
package annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 水果名稱注解 * @author wangsheng * */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface FruitName { String value() default ""; }

package annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 水果顏色注解 * @author peida * */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface FruitColor { /** * 顏色枚舉 * @author wangsheng * */ public enum Color{ BULE,RED,GREEN}; /** * 顏色屬性 * @return */ Color fruitColor() default Color.GREEN; }

package annotation; import annotation.FruitColor.Color; public class Apple { @FruitName("Apple") private String appleName; @FruitColor(fruitColor=Color.RED) private String appleColor; 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 displayName(){ System.out.println("水果的名字是:蘋果"); } }
注解元素的默認值:
注解元素必須有確定的值,要么在定義注解的默認值中指定,要么在使用注解時指定,非基本類型的注解元素的值不可為null。因此, 使用空字符串或0作為默認值是一種常用的做法。這個約束使得處理器很難表現一個元素的存在或缺失的狀態,因為每個注解的聲明中,所有元素都存在,並且都具有相應的值,為了繞開這個約束,我們只能定義一些特殊的值,例如空字符串或者負數,一次表示某個元素不存在,在定義注解時,這已經成為一個習慣用法

package annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 水果供應者注解 * @author wangsheng * */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface FruitProvider { /** * 供應商編號 * @return */ public int id() default -1; /** * 供應商名稱 * @return */ public String name() default ""; /** * 供應商地址 * @return */ public String address() default ""; }
定義了注解,並在需要的時候給相關類,類屬性加上注解信息,如果沒有響應的注解信息處理流程,注解可以說是沒有實用價值。如何讓注解真真的發揮作用,主要就在於注解處理方法,下一步我們將學習注解信息的獲取和處理
看完上面的例子后,來看一個更加具體的例子,該例子共涉及4個類:
清單1:Author.java

/** * */ package com.wsheng.aggregator.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 定義作者信息,name和group * * @author Josh Wang(Sheng) * * @email josh_wang23@hotmail.com * */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Documented public @interface Author { String name(); // 因為沒有定義public,所以默認的訪問權限為包權限,在定義時沒有指定默認值,則使用時必須指定默認值 String group(); }
清單2:Description.java

/** * */ package com.wsheng.aggregator.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 定義描述信息value * * @author Josh Wang(Sheng) * * @email josh_wang23@hotmail.com * */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented public @interface Description { String value();// 只有一個屬性時,最好定義為value,因為可以省略哦:) }
清單3:Utility.java : 使用自定義Annotation注解的類

/** * */ package com.wsheng.aggregator.annotation; /** * @author Josh Wang(Sheng) * * @email josh_wang23@hotmail.com * */ @Description(value="這是一個有用的工具類") // value可以省略 public class Utility { @Author(name="wangsheng", group="developer team") public String work() { return "work over!"; } }
清單4:AnalysisAnnotation.java

/** * */ package com.wsheng.aggregator.annotation; import java.lang.reflect.Method; /** * *在運行時分析處理annotation類型的信息 * * @author Josh Wang(Sheng) * * @email josh_wang23@hotmail.com * */ public class AnalysisAnnotation { public static void main(String[] args) { try { // 通過運行時反射API獲得annotation信息 Class<?> rtClass = Class.forName("com.wsheng.aggregator.annotation.Utility"); Method[] methods = rtClass.getMethods(); boolean descriptionExist = rtClass.isAnnotationPresent(Description.class); if (descriptionExist) { Description description = (Description)rtClass.getAnnotation(Description.class); System.out.println("Utility's Description --- > " + description.value()); for (Method method : methods) { if (method.isAnnotationPresent(Author.class)) { Author author = (Author)method.getAnnotation(Author.class); System.out.println("Utility's Author ---> " + author.name() + " from " + author.group()); } } } } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
運行結果:
Utility's Description --- > 這是一個有用的工具類
Utility's Author ---> wangsheng from developer team