深刻淺出Java Annotation(元註解和自定義註解)

1、基本概述 java

 

 

   Annontation是Java5開始引入的新特徵。中文名稱通常叫註解。它提供了一種安全的相似註釋的機制,用來將任何的信息或元數據(metadata)與程序元素(類、方法、成員變量等)進行關聯。 數組

 

  更通俗的意思是爲程序的元素(類、方法、成員變量)加上更直觀更明瞭的說明,這些說明信息是與程序的業務邏輯無關,而且是供指定的工具或框架使用的。 安全

 

    Annontation像一種修飾符同樣,應用於包、類型、構造方法、方法、成員變量、參數及本地變量的聲明語句中。 app

 

2、原理 框架

 

  Annotation實際上是一種接口。經過Java的反射機制相關的API來訪問Annotation信息。相關類(框架或工具中的類即便用註解的類)根據這些信息來決定如何使用該程序元素或改變它們的行爲。 ide

 

  Annoation和程序代碼的隔離性:Annotation是不會影響程序代碼的執行,不管Annotation怎麼變化,代碼都始終如一地執行。 工具

 

  忽略性:Java語言解釋器在工做時會忽略這些annotation,所以在JVM 中這些Annotation是「不起做用」的,只能經過配套的工具才能對這些Annontaion類型的信息進行訪問和處理。 學習

 

  Annotation與interface的異同: ui

 

  1)、Annotation類型使用關鍵字@interface而不是interface。 this

 

  這個關鍵字聲明隱含了一個信息:它是繼承了java.lang.annotation.Annotation接口,並不是聲明瞭一個interface。

 

  2)、Annotation類型、方法定義是獨特的、受限制的。

 

  Annotation 類型的方法必須聲明爲無參數無異常拋出的

     這些方法定義了Annotation的成員:方法名成爲了成員名,而方法返回值成爲了成員的類型。方法返回值類型必須爲primitive類型、Class類型、枚舉類型、annotation類型或者由前面類型之一做爲元素的一維數組。方法的後面可使用 default和一個默認數值來聲明成員的默認值,null不能做爲成員默認值,這與咱們在非Annotation類型中定義方法有很大不一樣。

 

  Annotation類型和它的方法不能使用Annotation類型的參數、成員不能是generic。只有返回值類型是Class的方法能夠在Annotation類型中使用generic,由於此方法可以用類轉換將各類類型轉換爲Class。

 

  3)、Annotation類型又與接口有着近似之處。

 

  它們能夠定義常量、靜態成員類型(好比枚舉類型定義)。Annotation類型也能夠如接口通常被實現或者繼承。

 

3、應用場合

 

  Annotation通常做爲一種輔助途徑,應用在軟件框架或工具中,在這些工具類中根據不一樣的 Annontation註解信息採起不一樣的處理過程或改變相應程序元素(類、方法及成員變量等)的行爲。

 

  例如:Junit、Struts、Spring等流行工具框架中均普遍使用了Annontion。使代碼的靈活性大提升。

 

4、元註解

      元註解的做用就是負責註解其餘註解。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可更加明晰其修飾的目標。

  做用:用於描述註解的使用範圍(即:被描述的註解能夠用在什麼地方)

  取值(ElementType)有:

    1.CONSTRUCTOR:用於描述構造器
    2.FIELD:用於描述域即類成員變量
    3.LOCAL_VARIABLE:用於描述局部變量
    4.METHOD:用於描述方法
    5.PACKAGE:用於描述包
    6.PARAMETER:用於描述參數
    7.TYPE:用於描述類、接口(包括註解類型) 或enum聲明

實例:

 

Java代碼   收藏代碼
  1. <strong style="font-size: 16px;">@Target(ElementType.TYPE)  
  2. public @interface Table {  
  3.     /** 
  4.      * 數據表名稱註解,默認值爲類名稱 
  5.      * @return 
  6.      */  
  7.     public String tableName() default "className";  
  8. }  
  9.   
  10. @Target(ElementType.FIELD)  
  11. public @interface NoDBColumn {  
  12.   
  13. }</strong>  

註解Table 能夠用於註解類、接口(包括註解類型)或enum聲明,而註解NoDBColumn僅可用於註解類的成員變量。

 


 @Retention:

  @Retention定義了該Annotation被保留的時間長短:某些Annotation僅出如今源代碼中,而被編譯器丟棄;而另外一些卻被編譯在class文件中;編譯在class文件中的Annotation可能會被虛擬機忽略,而另外一些在class被裝載時將被讀取(請注意並不影響class的執行,由於Annotation與class在使用上是被分離的)。使用這個meta-Annotation能夠對 Annotation的「生命週期」限制。

  做用:表示須要在什麼級別保存該註釋信息,用於描述註解的生命週期(即:被描述的註解在什麼範圍內有效)

  取值(RetentionPoicy)有:

    1.SOURCE:在源文件中有效(即源文件保留)
    2.CLASS:在class文件中有效(即class保留)
    3.RUNTIME:在運行時有效(即運行時保留)

  Retention meta-annotation類型有惟一的value做爲成員,它的取值來自java.lang.annotation.RetentionPolicy的枚舉類型值。具體實例以下:

 

Java代碼   收藏代碼
  1. @Target(ElementType.FIELD)  
  2. @Retention(RetentionPolicy.RUNTIME)  
  3. public @interface Column {  
  4.     public String name() default "fieldName";  
  5.     public String setFuncName() default "setField";  
  6.     public String getFuncName() default "getField";   
  7.     public boolean defaultDBValue() default false;  
  8. }  

 Column註解的的RetentionPolicy的屬性值是RUTIME,這樣註解處理器能夠經過反射,獲取到該註解的屬性值,從而去作一些運行時的邏輯處理。


 @Documented:

  @Documented用於描述其它類型的annotation應該被做爲被標註的程序成員的公共API,所以能夠被例如javadoc此類的工具文檔化。Documented是一個標記註解,沒有成員。

 

 

Java代碼   收藏代碼
  1. @Target(ElementType.FIELD)  
  2. @Retention(RetentionPolicy.RUNTIME)  
  3. @Documented  
  4. public @interface Column {  
  5.     public String name() default "fieldName";  
  6.     public String setFuncName() default "setField";  
  7.     public String getFuncName() default "getField";   
  8.     public boolean defaultDBValue() default false;  
  9. }  

 @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類型被發現,或者到達類繼承結構的頂層.

實例代碼:

 

Java代碼   收藏代碼
  1. <span style="font-size: 16px;">/** 
  2.  *  
  3.  * @author wangsheng 
  4.  * 
  5.  */  
  6. @Inherited  
  7. public @interface Greeting {  
  8.     public enum FontColor{ BULE,RED,GREEN};  
  9.     String name();  
  10.     FontColor fontColor() default FontColor.GREEN;  
  11. }</span>  

 

 

5、常見標準的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的成員賦值:

代碼:

 

Java代碼   收藏代碼
  1. @SuppressWarnings(value={"unchecked","fallthrough"})  
  2.   
  3. public void lintTrap() { /* sloppy method body omitted */ }  

 

 

   在這個例子中SuppressWarnings annotation類型只定義了一個單一的成員,因此只有一個簡單的value={...}做爲name=value對。又因爲成員值是一個數組,故使用大括號來聲明數組值。

 

 

 

   注意:咱們能夠在下面的狀況中縮寫annotation:當annotation只有單一成員,併成員命名爲"value="。這時能夠省去"value="。好比將上面的SuppressWarnings annotation進行縮寫:

 

代碼:

 

Java代碼   收藏代碼
  1. @SuppressWarnings({"unchecked","fallthrough"})  

 

 

若是SuppressWarnings所聲明的被禁止警告個數爲一個時,能夠省去大括號:

 

Java代碼   收藏代碼
  1. @SuppressWarnings("unchecked")  

 

 

6、自定義註解

     使用@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註解就只有一個參數成員。

簡單的自定義註解和使用註解實例:

 

Java代碼   收藏代碼
  1. package annotation;  
  2.   
  3. import java.lang.annotation.Documented;  
  4. import java.lang.annotation.ElementType;  
  5. import java.lang.annotation.Retention;  
  6. import java.lang.annotation.RetentionPolicy;  
  7. import java.lang.annotation.Target;  
  8.   
  9. /** 
  10.  * 水果名稱註解 
  11.  * @author wangsheng 
  12.  * 
  13.  */  
  14. @Target(ElementType.FIELD)  
  15. @Retention(RetentionPolicy.RUNTIME)  
  16. @Documented  
  17. public @interface FruitName {  
  18.     String value() default "";  
  19. }  

 

 

 

Java代碼   收藏代碼
  1. package annotation;  
  2.   
  3. import java.lang.annotation.Documented;  
  4. import java.lang.annotation.ElementType;  
  5. import java.lang.annotation.Retention;  
  6. import java.lang.annotation.RetentionPolicy;  
  7. import java.lang.annotation.Target;  
  8.   
  9. /** 
  10.  * 水果顏色註解 
  11.  * @author peida 
  12.  * 
  13.  */  
  14. @Target(ElementType.FIELD)  
  15. @Retention(RetentionPolicy.RUNTIME)  
  16. @Documented  
  17. public @interface FruitColor {  
  18.     /** 
  19.      * 顏色枚舉 
  20.      * @author wangsheng 
  21.      * 
  22.      */  
  23.     public enum Color{ BULE,RED,GREEN};  
  24.       
  25.     /** 
  26.      * 顏色屬性 
  27.      * @return 
  28.      */  
  29.     Color fruitColor() default Color.GREEN;  
  30.   
  31. }  

 

Java代碼   收藏代碼
  1. package annotation;  
  2.   
  3. import annotation.FruitColor.Color;  
  4.   
  5. public class Apple {  
  6.       
  7.     @FruitName("Apple")  
  8.     private String appleName;  
  9.       
  10.     @FruitColor(fruitColor=Color.RED)  
  11.     private String appleColor;  
  12.       
  13.       
  14.       
  15.       
  16.     public void setAppleColor(String appleColor) {  
  17.         this.appleColor = appleColor;  
  18.     }  
  19.     public String getAppleColor() {  
  20.         return appleColor;  
  21.     }  
  22.       
  23.       
  24.     public void setAppleName(String appleName) {  
  25.         this.appleName = appleName;  
  26.     }  
  27.     public String getAppleName() {  
  28.         return appleName;  
  29.     }  
  30.       
  31.     public void displayName(){  
  32.         System.out.println("水果的名字是:蘋果");  
  33.     }  
  34. }  

 註解元素的默認值:

 

    註解元素必須有肯定的值,要麼在定義註解的默認值中指定,要麼在使用註解時指定,非基本類型的註解元素的值不可爲null。所以, 使用空字符串或0做爲默認值是一種經常使用的作法。這個約束使得處理器很難表現一個元素的存在或缺失的狀態,由於每一個註解的聲明中,全部元素都存在,而且都具備相應的值,爲了繞開這個約束,咱們只能定義一些特殊的值,例如空字符串或者負數,一次表示某個元素不存在,在定義註解時,這已經成爲一個習慣用法。例如:

Java代碼   收藏代碼
  1. <strong style="margin: 0px; padding: 0px;">package annotation;  
  2.   
  3. import java.lang.annotation.Documented;  
  4. import java.lang.annotation.ElementType;  
  5. import java.lang.annotation.Retention;  
  6. import java.lang.annotation.RetentionPolicy;  
  7. import java.lang.annotation.Target;  
  8.   
  9. /** 
  10.  * 水果供應者註解 
  11.  * @author wangsheng 
  12.  * 
  13.  */  
  14. @Target(ElementType.FIELD)  
  15. @Retention(RetentionPolicy.RUNTIME)  
  16. @Documented  
  17. public @interface FruitProvider {  
  18.     /** 
  19.      * 供應商編號 
  20.      * @return 
  21.      */  
  22.     public int id() default -1;  
  23.       
  24.     /** 
  25.      * 供應商名稱 
  26.      * @return 
  27.      */  
  28.     public String name() default "";  
  29.       
  30.     /** 
  31.      * 供應商地址 
  32.      * @return 
  33.      */  
  34.     public String address() default "";  
  35. }</strong>  

 

 

定義了註解,並在須要的時候給相關類,類屬性加上註解信息,若是沒有響應的註解信息處理流程,註解能夠說是沒有實用價值。如何讓註解真真的發揮做用,主要就在於註解處理方法,下一步咱們將學習註解信息的獲取和處理!

 

 

看完上面的例子後,來看一個更加具體的例子,該例子共涉及4個類:

清單1:Author.java

Java代碼   收藏代碼
  1. /** 
  2.  *  
  3.  */  
  4. package com.wsheng.aggregator.annotation;  
  5.   
  6. import java.lang.annotation.Documented;  
  7. import java.lang.annotation.ElementType;  
  8. import java.lang.annotation.Retention;  
  9. import java.lang.annotation.RetentionPolicy;  
  10. import java.lang.annotation.Target;  
  11.   
  12. /** 
  13.  * 定義做者信息,name和group 
  14.  *  
  15.  * @author Josh Wang(Sheng) 
  16.  *  
  17.  * @email  josh_wang23@hotmail.com 
  18.  *  
  19.  */  
  20. @Retention(RetentionPolicy.RUNTIME)  
  21. @Target(ElementType.METHOD)  
  22. @Documented  
  23. public @interface Author {  
  24.       
  25.     String name(); // 由於沒有定義public,因此默認的訪問權限爲包權限,在定義時沒有指定默認值,則使用時必須指定默認值  
  26.     String group();  
  27.   
  28. }  

 

 

清單2:Description.java

 

Java代碼   收藏代碼
  1. /** 
  2.  *  
  3.  */  
  4. package com.wsheng.aggregator.annotation;  
  5.   
  6. import java.lang.annotation.Documented;  
  7. import java.lang.annotation.ElementType;  
  8. import java.lang.annotation.Retention;  
  9. import java.lang.annotation.RetentionPolicy;  
  10. import java.lang.annotation.Target;  
  11.   
  12. /** 
  13.  * 定義描述信息value 
  14.  *  
  15.  * @author Josh Wang(Sheng) 
  16.  *  
  17.  * @email  josh_wang23@hotmail.com 
  18.  *  
  19.  */  
  20. @Retention(RetentionPolicy.RUNTIME)  
  21. @Target(ElementType.TYPE)  
  22. @Documented  
  23. public @interface Description {  
  24.   
  25.     String value();// 只有一個屬性時,最好定義爲value,由於能夠省略哦:)  
  26. }  

 

 

 

清單3:Utility.java : 使用自定義Annotation註解的類

 

Java代碼   收藏代碼
  1. /** 
  2.  *  
  3.  */  
  4. package com.wsheng.aggregator.annotation;  
  5.   
  6. /** 
  7.  * @author Josh Wang(Sheng) 
  8.  *  
  9.  * @email  josh_wang23@hotmail.com 
  10.  *  
  11.  */  
  12. @Description(value="這是一個有用的工具類"// value能夠省略  
  13. public class Utility {  
  14.   
  15.     @Author(name="wangsheng", group="developer team")  
  16.     public String work() {  
  17.         return "work over!";  
  18.     }  
  19. }  

 

 

清單4:AnalysisAnnotation.java

 

Java代碼   收藏代碼
  1. /** 
  2.  *  
  3.  */  
  4. package com.wsheng.aggregator.annotation;  
  5.   
  6. import java.lang.reflect.Method;  
  7.   
  8. /** 
  9.  *  
  10.  *在運行時分析處理annotation類型的信息 
  11.  *  
  12.  * @author Josh Wang(Sheng) 
  13.  *  
  14.  * @email  josh_wang23@hotmail.com 
  15.  *  
  16.  */  
  17. public class AnalysisAnnotation {  
  18.       
  19.     public static void main(String[] args) {  
  20.         try {  
  21.               
  22.             // 經過運行時反射API得到annotation信息  
  23.             Class<?> rtClass = Class.forName("com.wsheng.aggregator.annotation.Utility");  
  24.             Method[] methods = rtClass.getMethods();  
  25.               
  26.             boolean descriptionExist = rtClass.isAnnotationPresent(Description.class);  
  27.             if (descriptionExist) {  
  28.                 Description description = (Description)rtClass.getAnnotation(Description.class);  
  29.                 System.out.println("Utility's Description --- > " + description.value());  
  30.                   
  31.                 for (Method method : methods) {  
  32.                     if (method.isAnnotationPresent(Author.class)) {  
  33.                         Author author = (Author)method.getAnnotation(Author.class);  
  34.                         System.out.println("Utility's Author ---> " + author.name() + " from " + author.group());  
  35.                     }  
  36.                 }  
  37.             }  
  38.               
  39.         } catch (ClassNotFoundException e) {  
  40.             e.printStackTrace();  
  41.         }  
  42.     }  
  43.   
  44. }  

 

運行結果:

Utility's Description --- > 這是一個有用的工具類

 

Utility's Author ---> wangsheng from developer team

相關文章
相關標籤/搜索