
寫在前面
JDK5增加了對Annotation(注解)的支持,Annotation是代碼里的特殊標記,這些標記可以在編譯,類加載和運行時被讀取讀取出來,并執(zhí)行相應的處理和操作!比如在不改變程序邏輯的情況下,開發(fā)人員可以在代碼中嵌入一些補充信息,代碼分析和開發(fā)部署工具APT(AnnotationProcessTool)可以通過這些信息進行驗證或部署。
APT工具在處理注解時,會根據源文件的Annotation信息,生成附加源文件或者進行一些其他操作!最后在車鞥徐編譯時期,APT會將生成的附加源文件和原有源文件進行合并,生成最終的*.class文件!默認情況下,Annotation可以修飾任何程序元素(ElementType),包括類,接口,成員方法,成員變量。根據Annotation是否帶成員變量,可分為兩類:
- 標記Annotation,根據自身存在與否提供信息;
- 元數(shù)據Annotation,根據接收的成員變量值,提供元數(shù)據信息;
設計Annotation
與定義一個接口很相似,自定義Annotation使用@interface關鍵字:
//字符定義Annotation
public @interface CustomAnno {
}
//使用上面自定義的Annotaiton,修飾類
@CustomAnno
public class BeautifulClass {
//修飾成員方法
@CustomAnno
public void toPlus() {...}
}
Annotation也可以有成員變量,如下定義:
public @interface HelloAnno {
//定義帶兩個成員變量的Annotation
//Annotaion中的成員變量以方法的形式來定義
String apple();
Integer pear();
}
//使用帶成員變量的Annotation
public class beautifulClass2 {
//需要為成員變量賦值
@HelloAnno(apple="apple", pear=101)
public void toPlus() {...}
}
當然,帶成員變量的Annotation也可以為期指定初始值(默認值),在哪在使用注解的時候就不需要再次未改成員變量賦值了;當然,如果需要的話也可以再次賦值,則新值會覆蓋默認值!如下:
public @interface BlackAnno {
//定義帶兩個成員變量的Annotation
//Annotaion中的成員變量以方法的形式來定義
//指定成員變量的默認值,使用default關鍵字
String apple() default "black";
Integer pear() default 101;
}
//使用帶成員變量的Annotation
public class beautifulClass3 {
//@HelloAnno已經指定了默認值
//則使用時不需要賦值,直接使用即可
@HelloAnno
public void toPlus1() {...}
//如果需要,也可以為成員變量賦值,則會覆蓋默認值
@HelloAnno(apple="apple", pear=101)
public void toPlus2() {...}
}
提取Annotation
使用Annotation修飾了類以及成員方法后,這些Annotation不會自己生效,必須有開發(fā)者提供相應的工具來提取并處理Annotation信息。
java.lang.reflect包下包含一些實現(xiàn)反射功能的工具類:比如定義Annotation時使用@Rentation(RetentionPolicy.RUNTIME)修飾,則Annotation會被編譯器保存在*.class文件中,Java在運行時會通過反射讀取類文件中的Annotation!
AnnotatedElement接口是所有程序元素(Class,Method,Constructor等)的父接口,所以獲得某個AnnotationElement對象后,就可以調用該對象的如下方法來訪問Annotation注解信息:
<T extends Annotation> T getAnnotation(Class<T> annotationClass)Annotation[] getAnnotations()default <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass)default <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass)Annotation[] getDeclaredAnnotations()default <T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass)default boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
自定義注解demo
自定義一個Java注解 @CustomAnno,代碼如下:
/**
* 自定義一個 Java 注解
* 1. @Retention(RetentionPolicy.RUNTIME)
* 運行時讀取并處理注解信息
* 2. @Target(ElementType.METHOD)
* 自定義注解只能修飾方法
*
* Created by wondertwo on 2016/10/16.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface CustomAnno {}
用以上自定義的注解,來修飾Person類的八個方法,代碼如下:
/**
* Created by wondertwo on 2016/10/16.
*/
public class Person {
/**
* 使用自定義注解 @CustomAnno 修飾以下 8個方法
*/
@CustomAnno
public static void method_1() {}
@CustomAnno
public static void method_2() {}
@CustomAnno
public static void method_3() {}
@CustomAnno
public static void method_4() {}
@CustomAnno
public static void method_5() {}
@CustomAnno
public static void method_6() {}
@CustomAnno
public static void method_7() {}
@CustomAnno
public static void method_8() {}
}
測試注解,通過反射調用方法,代碼如下:
/**
* Created by wondertwo on 2016/10/16.
*/
public class AnnoTest {
public static void main(String[] args) throws ClassNotFoundException {
process("me.wondertwo.annotation.Person"); //Person
}
private static void process(String clazz) throws ClassNotFoundException {
int passed= 0 , failed = 0;
//遍歷clazz對應類的所有方法
for (Method method : Class.forName(clazz).getMethods()) {
//如果該方法使用了自定義注解 @CustomAnno 修飾
if (method.isAnnotationPresent(CustomAnno.class)) {
try {
method.invoke(null);
passed++;
} catch (Exception e) {
e.printStackTrace();
System.out.println("方法 " + method + " 測試失敗,出現(xiàn)異常");
failed++;
}
}
}
System.out.println("一共運行了 " + (passed+failed) + " 個方法");
System.out.println("成功運行方法 " + passed + " 個");
System.out.println("失敗運行方法 " + failed + " 個");
}
}
/**
* 運行結果輸出如下:
* 一共運行了 8 個方法
* 成功運行方法 8 個
* 失敗運行方法 0 個
* Process finished with exit code 0
*/
我們的自定義注解,成功的被提取出來并執(zhí)行了!