天天看點

全面解析Java注解1.  了解注解 2.  定義注解 3.  使用注解 4.  解析注解

1.  了解注解

我們有必要對JDK 5.0新增的注解(Annotation)技術進行簡單的學習,因為Spring 支援@AspectJ,而@AspectJ本身就是基于JDK 5.0的注解技術。是以學習JDK 5.0的注解知識有助于我們更好地了解和掌握Spring的AOP技術。

對于Java開發人員來說,在編寫代碼時,除了源程式以外,我們還會使用 Javadoc标簽對類、方法或成員變量進行注釋,以便使用Javadoc工具生成和源代碼配套的Javadoc文檔。這些@param、@return 等Javadoc标簽就是注解标簽,它們為第三方工具提供了描述程式代碼的注釋資訊。使用過Xdoclet的朋友,對此将更有感觸,像Struts、 Hibernate都提供了Xdoclet标簽,使用它們可以快速地生成對應程式代碼的配置檔案。

JDK5.0注解可以看成是Javadoc标簽和Xdoclet标簽的延伸和發展。在JDK5.0中,我們可以自定義這些标簽,并通過Java語言的反射機制中擷取類中标注的注解,完成特定的功能。

注解是代碼的附屬資訊,它遵循一個基本原則:注解不能直接幹擾程式代碼的運作,無論增加或删除注解,代碼都能夠正常運作。Java語言解釋器會忽略這些注解,而由第三方工具負責對注解進行處理。第三方工具可以利用代碼中的注解間接控制程式代碼的運作,它們通過Java反射機制讀取注解的資訊,并根據這些資訊更改目标程式的邏輯,而這正是Spring AOP對@AspectJ提供支援所采取的方法。

注解的文法比較簡單,除了@符号的使用以外,它基本上與java的固有文法一緻,java内置了三種注解,定義在java.lang包中。

@Override:隻能用在方法之上的,用來告訴别人這一個方法是改寫父類的。

@Deprecated:建議别人不要使用舊的API的時候用的,編譯的時候會用産生警告資訊,可以設定在程式裡的所有的元素上。

@SuppressWarnings:表示關閉一些不當的編譯器警告資訊。

2.  定義注解

下面是一個定義注解的執行個體。

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Inherited

public @interface Description {

 String value();

}

其中的@interface是一個關鍵字,在設計annotations的時候必須把一個類型定義為@interface,而不能用class或interface關鍵字。所有的注解類都隐式繼承于 java.lang.annotation.Annotation,注解不允許顯式繼承于其他的接口。

一個注解可以擁有多個成員,成員聲明和接口方法聲明類似,這裡,我們僅定義了一個成員,成員的聲明有以下幾點限制:

a)   成員以無入參無抛出異常的方式聲明,如boolean value(String str)、boolean value() throws Exception等方式是非法的;

b)   可以通過default為成員指定一個預設值,如String level() default "LOW_LEVEL"、int high() default 2是合法的,當然也可以不指定預設值;

c)   成員類型是受限的,合法的類型包括原始類型及其封裝類、String、Class、enums、注解類型,以及上述類型的數組類型。如ForumService value()、List foo()是非法的。

d)   如果注解隻有一個成員,則成員名必須取名為value(),在使用時可以忽略成員名和指派号(=),如@Description("使用注解的執行個體")。注解類擁有多個成員時,如果僅對value成員進行指派則也可不使用指派号,如果同時對多個成員進行指派,則必須使用指派号,如@DeclareParents (value = "NaiveWaiter", defaultImpl = SmartSeller.class)。

e)   注解類可以沒有成員,沒有成員的注解稱為辨別注解,解釋程式以辨別注解存在與否進行相應的處理;

注解定義包含四個元注解,分别為@Target,@Retention,@Documented,@Inherited。各元注解的作用如下:

1)  @Target

表示該注解用于什麼地方,可能的 ElemenetType 參數包括:

Ø  ElemenetType.CONSTRUCTOR 構造器聲明。

Ø  ElemenetType.FIELD 域聲明(包括 enum 執行個體)。

Ø  ElemenetType.LOCAL_VARIABLE 局部變量聲明。

Ø  ElemenetType.METHOD 方法聲明。

Ø  ElemenetType.PACKAGE 包聲明。

Ø  ElemenetType.PARAMETER 參數聲明。

Ø  ElemenetType.TYPE 類,接口(包括注解類型)或enum聲明。

2)  @Retention

表示在什麼級别儲存該注解資訊。可選的 RetentionPolicy 參數包括:

Ø  RetentionPolicy.SOURCE 注解将被編譯器丢棄。

Ø  RetentionPolicy.CLASS 注解在class檔案中可用,但會被VM丢棄。

Ø  RetentionPolicy.RUNTIME VM将在運作期也保留注釋,是以可以通過反射機制讀取注解的資訊。

舉一個例子,如@Override裡面的Retention設為SOURCE,編譯成功了就不要這一些檢查的資訊,相反,@Deprecated裡面的Retention設為RUNTIME,表示除了在編譯時會警告我們使用了哪個被 Deprecated的方法,在執行的時候也可以查出該方法是否被Deprecated。

3)  @Documented

将此注解包含在 javadoc 中

4)  @Inherited

允許子類繼承父類中的注解

3.  使用注解

下面是一個使用注解注解的執行個體。

@Description(value="使用注解的執行個體")

public class TestAnnotation {

使用注解的文法:@<注解名>(<成員名1>=<成員值1>,<成員名1>=<成員值1>,...)

如果成員是數組類型,可以通過{}進行指派,如boolean數組的成員可以設定為{true,false,true}。下面是幾個注解标注的例子:

1)   多成員的注解

@AnnoExample(id= 2868724, synopsis = "Enable time-travel", engineer = "Mr. Peabody")

2)   一個成員的注解,成員名為value

可以省略成員名和指派符号:@Copyright("Right Reserved")

@Copyright("2011 bookegou.com All Right Reserved")

3)   無成員的注解

@Override

4)   成員為字元串數組的注解

@SuppressWarnings(value={"unchecked","fallthrough"})

5)   成員為注解數組類型的注解

@Reviews({@Review(grade=Review.Grade.EXCELLENT,reviewer="df"),

@Review(grade=Review.Grade.UNSATISFACTORY,reviewer="eg", 

comment="This method needs an @Override annotation")})

@Reviews({@Review(grade=Review.Grade.EXCELLENT,reviewer="df"),     

           @Review(grade=Review.Grade.UNSATISFACTORY,reviewer="eg",             

                    comment="This method needs an @Override annotation")})

@Reviews注解擁有一個@Review注解數組類型的成員,@Review注解類型有三個成員,其中reviewer、comment都是String類型,但comment有預設值,grade是枚舉類型的成員。

4.  解析注解

下面是一個解析注解的執行個體。

public class ParseAnnotation {

 public static void main(String[] args) throws Exception{

  final Class cls = Class.forName("com.annotation.TestAnnotation");

  final Method[] method = cls.getMethods();

  // 判斷是否有指定注解類型的注解

  if(cls.isAnnotationPresent(Description.class)) {

   // 根據注解類型傳回指定類型的注解

   Description des = (Description)cls.getAnnotation(Description.class);

   System.out.println("注解描述:" + des.value());

  }

 }

輸出結果如下:

 注解描述:使用注解的執行個體

前面提到過,注解不會直接影響程式的運作,但是第三方程式或工具可以利用代碼中的注解完成特殊的任務,間接控制程式的運作。對于RetentionPolicy.RUNTIME保留期限的注解,我們可以通過反射機制通路類中的注解。

在JDK5.0裡,Package、Class、Constructor、 Method以及Field等反射對象都新增了通路注解資訊的方法:<T extends Annotation>T getAnnotation(Class<T> annotationClass),該方法支援通過泛型直接傳回注解對象。