寫在前面
JDK5增加了對Annotation(注解)的支持辑鲤,Annotation是代碼里的特殊標記,這些標記可以在編譯杠茬,類加載和運行時被讀取讀取出來月褥,并執(zhí)行相應的處理和操作!比如在不改變程序邏輯的情況下瓢喉,開發(fā)人員可以在代碼中嵌入一些補充信息吓坚,代碼分析和開發(fā)部署工具APT(AnnotationProcessTool)可以通過這些信息進行驗證或部署。
APT工具在處理注解時灯荧,會根據(jù)源文件的Annotation信息,生成附加源文件或者進行一些其他操作盐杂!最后在車鞥徐編譯時期逗载,APT會將生成的附加源文件和原有源文件進行合并哆窿,生成最終的*.class文件!默認情況下厉斟,Annotation可以修飾任何程序元素(ElementType)挚躯,包括類,接口擦秽,成員方法码荔,成員變量。根據(jù)Annotation是否帶成員變量感挥,可分為兩類:
- 標記Annotation缩搅,根據(jù)自身存在與否提供信息;
- 元數(shù)據(jù)Annotation触幼,根據(jù)接收的成員變量值硼瓣,提供元數(shù)據(jù)信息;
設計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已經(jīng)指定了默認值
//則使用時不需要賦值镜沽,直接使用即可
@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對象后,就可以調(diào)用該對象的如下方法來訪問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() {}
}
測試注解,通過反射調(diào)用方法黄锤,代碼如下:
/**
* 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í)行了鸵熟!