Java中Annotation(註釋)系列學習筆記(2)

二.自定義Annotation
  前面已經介紹瞭如何使用java.lang包下的三個標準Annotation。下面介紹如何定義 Annotation,並利用Annotation完成一些實際的功能。

(1)定義Annotation
  定義新的Annotation類型時使用@interface關健字(在原有的interface關健字前增加@符號),它用於定義新的Annotation類型。定義一個新的Annotation類型與定義一個接口非常像。

如下代碼定義一個簡單的Annotation
//定義一個簡單的Annotation類型
public @interface Test
{
}
  定義該Annotation之後,就可以在程序任何地方使用該Annotation ,使用Annotation時的語法非常類似於public ,final這樣的修飾符,通常可以用於修飾程序中的類,方法,變量,接口等定義。
  通常我們會把Annotation放在所有修飾符之前,而且由於使用Annotation時可能還需要爲其它成員變量指定值,因而Annotation的長度可能較長,所以通常把Annotaion另放一行.

//使用@Test修飾類定義
@Test
public class MyClass
{
...
}

默認情況下,Annotation可用於修飾任何元素,包括類,接口,方法等。如下程序使用@TestAnnotation來修飾方法
public class MyClass
{
   //使用@TestAnnotation修飾方法
   @Test
   public void info()
   {
 ...
   }
}

  Annotation不僅可以是這種簡單的Annotation,Annotation還可以帶用員變量,Annotation的成員變量在Annotation定義中以無參數來聲明。其方法和返回值定義了該成員的名字和類型。
如下代碼可以定義一個有成員變量的Annotation
public @interface MyTag
{
    //定義兩個成員變量Annotation
    //Annotation中的成員變量以方法的形式來定義
    String name();
    int age();
}

  仔細觀察:上面定義Annotation的代碼與定義接口的語法非常像,只是上面MyTag使用@interface關健字來定義,而接口使用interface來定義

  一旦在Annotation裏定義了成員變量之後,使用該Annotation時應該爲該Annotation的成員變量指定值,如下代碼所示
public class Test
{
    //使用帶成員變量的Annotation時,需要爲成員變量付值
    MyTag(name='heyitang',age=30)
    public void info()
    {
 ...
    }
}

  我們還可以在定義Annotation的成員變量時爲其指定初始值,指定成員變量的初始值可使用default關健字。如下代碼定義了MyTag Annotaion,該Annotation裏包含了兩個成員:name和age,這兩個成元變量使用default指定了默認值
public @interface MyTag
{
   //定義了兩個成員變量的Annotaion
   //以default爲兩個成員變量指定初始值
   String name() default "yeeku";
   int age() default 32;
}
 
  如果爲Annotation的成員變量指定了默認值,使用該Annotation則可以不爲這些成員變量指定值,而是直接使用默認值,如下代碼所示
public class Test
{
    //使用帶成員變量的Annotation
    //因爲它的成員變量有默認值,所以可以無須爲成員變量指定值
    @MyTag
    public void info()
    {
        ...
    }
}

    當然我們介紹的Annotation是否可以包含成員指定值,如果MyTag爲的成員變量指定了值,則默認值不會起作用
    總結
      根據我們介紹的Annotation是否可以包含成員變量,我們可以把Annotation分爲如下兩類
      1.
標記Annotation:一個沒有成員變量的Annotation類型被稱爲標記。這種Annotation僅合使用自身的存在與否來爲我們提供信息。如前面介紹的@Override,@Test等Annotation
      2.元數據Annotation:那些包含成員變量的Annotation,因爲它們可接受更多元數據,所以也被稱爲元數據Annotation


(2)提取Annotation
  前面已經提到:JAVA使用Annotation接口來代表元素前面的註釋,該接品是所有Annotation類型的父接口。除此之外,JAVA在java.lang.reflect包下新增了AnnotationElement接口,該接口代表程序中可以接受註釋的程序元素,該接口主要有如下幾個實現類.

    Class:類定義
    Constructor:構造器定義
    Field:類的成員變量定義
    Method:類的方法定義
    Package:類的包定義

    AnnotationElement接口是所有程序元素(如Class,Method,Constructor)的父接口,所以程序通過反射獲取某個類的了AnnotationElement對象(如Class,Method,和Constructor)之後,程序就可以調用該對象的如下三個方法來訪問Annotation信息


   getAnnotation(Class<T> annotationClass):返回該程序元素上存在的,指定類型的註釋
   Annotation[] getAnnotations:返回該程序元素上存在的所有註釋
   boolean isAnnotationPreset(Class<? extends Annotation> annotationClass):判斷該程序元素上是否包含指定類型的註釋,存在則返回true,否則返回false

 

(3)使用Annotation的例子
  下面介紹兩個使用Annotation的例子,第一個Annotation Testable沒有任何成員變量,僅是一個標記Annotation,它的作用是標記哪些方法是可測試的.
Testable.java源文件

import java.lang.annotation.*;
 
@Retention(RetentionPolicy.RUNTIME)   
@Target(ElementType.METHOD)
//定義Testable Annotation將被javadoc工具提取
@Documented
public @interface Testable
{
}

  上面程序定義了一個標記Testable Annotation,定義該Annotation時使用了@Retention和@Target兩個系統元註釋,其中@Retention註釋指明Testabel註釋可以保留多久,而@Target註釋指定Testable能修飾的目標(只能是方法)。

  如下MyTest測試用例裏定義了8個方法,這8個方法沒有太大的區別,其中四個方法使用@testable註釋來標記這些方法是可測試的
public class MyTest
{
 //使用@Testable標記註釋指定該方法是可測試的
 @Testable
 public static void m1()
 {
 }
 public static void m2()
 {
 }  
 //使用@Testable標記註釋指定該方法是可測試的
 @Testable
 public static void m3()
 {       
  throw new RuntimeException("Boom"); 
 }
 public static void m4()
 {
 }      
 //使用@Testable標記註釋指定該方法是可測試的
 @Testable
 public static void m5()
 {
 } 
    public static void m6()
 {
 }
 //使用@Testable標記註釋指定該方法是可測試的
 @Testable
 public static void m7()
 {           
  throw new RuntimeException("Crash");  
 }       
 public static void m8()
 {
 }
}


  正如前面提到的,僅僅使用註釋來標記程序元素對程序是不會有任何影響的,這也是註釋的一條重要原則,爲了讓程序中這些註釋起作用,我們必須爲這些註釋提供一個註釋處理工具。
  下面的註釋處理工具分析目標類,如果目標類中方法使用了@Testable註釋修飾,則通過反射來運行該測試方法

import java.lang.reflect.*;
public class TestProcessor
{
 public static void process(String clazz)
  throws ClassNotFoundException
 {
  int passed = 0;
  int failed = 0;
  //遍歷obj對象的所有方法
  for (Method m : Class.forName(clazz).getMethods())
  {
   //如果包含@Testable標記註釋
   if (m.isAnnotationPresent(Testable.class))
   {
    try
    {
     //調用m方法
     m.invoke(null);
     //passed加1
     passed++;
    }
    catch (Exception ex)
    {
     System.out.printf("方法" + m + "運行失敗,異常:" + ex.getCause() + "\n");
     failed++;
    }
   }
  }
  //統計測試結果
  System.out.printf("共運行了:" + (passed + failed)+ "個方法,其中:\n" +
   "失敗了:" + failed + "個,\n" + 
   "成功了:" + passed + "個!\n");
 }
}

  該TestProcessor類裏只包含了一個process方法,該方法可接受一個字符串參數,該方法將會分析clazz參數所代表的類,並運行該類裏的,使用@Testabel註釋修飾的方法

 

該程序的主類比較簡單,只提供主方法
import java.lang.reflect.*;
public class RunTests
{
 public static void main(String[] args) throws Exception
 {
  //處理MyTest類
  TestProcessor.process("MyTest");
 }
}

 

運行最終效果圖如下




總結
    通過這個運行結果可以看出,程序中的@Testable Annotation起作用了,MyTest類裏以@Testable註釋修飾的方法被正常測試

轉自:http://blog.sina.com.cn/heyitang

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章