二.自定義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註釋修飾的方法被正常測試了