在開發過程中總能用到註解,但是從來沒有自己定義過註解。最近賦閑在家,研究整理了一番,力求知其然知其所以然。
本文會嘗試描述什麼是註解,以及通過一個Demo來說明如何在程式中自訂註解。Demo沒有實際意義,僅僅只是為了註解而使用註解。
1、什麼是註解。
註解是在jdk 1.5開始提供的功能,目前被廣泛使用。以下是引用《Java瘋狂講義》第十四章關於註解的描述:
“Annotation其實是代碼裡的特殊標記,這些標記可以在編譯、類載入、運行時被讀取,並執行響應的處理。通過使用Annotation,程式開發人員可以在不改變原有邏輯的情況下,在源檔案嵌入一些補充資訊。程式碼分析工具、開發工具和部署工具可以通過這些補充資訊進行驗證或者進行部署。”
JUnit單元測試就是基於註解的。我認為使用動態代理和註解,自己完全也可以寫一個MyJUnit。
2、Demo的實現:
註解檔案:
這個檔案主要包含了三個部分:
A.註解檔案使用@interface來聲明。
B.在A外層有元註解聲明(註解的註解),主要的作用就是對我們自訂註解的執行範圍、時期進聲明。下面是註解的註解含義說明:
@Documented:指示某一類型的注釋將通過 javadoc 和類似的預設工具進行文檔化。應使用此類型來注釋這些類型的聲明:其注釋會影響由其用戶端注釋的元素的使用。如果型別宣告是用 Documented 來注釋的,則其注釋將成為注釋元素的公用 API 的一部分。
@Target:指示注釋類型所適用的程式元素的種類。如果注釋型別宣告中不存在 Target 元注釋,則聲明的類型可以用在任一程式元素上。如果存在這樣的元注釋,則編譯器強制實施指定的使用限制。例如,此元注釋指示該宣告類型是其自身,即元注釋類型。它只能用在注釋型別宣告上。
@Inherited:指示注釋類型被自動繼承。如果在注釋型別宣告中存在 Inherited 元注釋,並且使用者在某一類聲明中查詢該注釋類型,同時該類聲明中沒有此類型的注釋,則將在該類的超類中自動查詢該注釋類型。此過程會重複進行,直到找到此類型的注釋或到達了該類階層的頂層 (Object) 為止。如果沒有超類具有該類型的注釋,則查詢將指示當前類沒有這樣的注釋。注意,如果使用注釋類型注釋類以外的任何事物,此元注釋類型都是無效的。還要注意,此元注釋僅促成從超類繼承注釋;對已實現介面的注釋無效。
@Retention:指示注釋類型的注釋要保留多久。如果注釋型別宣告中不存在 Retention 注釋,則保留原則預設為 RetentionPolicy.CLASS。
C.方法名就是參數名,傳回值類型就是參數的類型,通過default來設定方法的預設值。
下面是代碼:
package com.array7.annotationTest;import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Inherited;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Documented@Target(ElementType.METHOD)@Inherited@Retention(RetentionPolicy.RUNTIME)public @interface MethodInfo {String author() default "array7";// 作者String date();// 日期double version() default 1.0;// 版本號碼String comments();// 注釋}
然後定義個調用註解的類:
package com.array7.annotationTest;public class AnnotationExample {/** * 非靜態方法 * @param msg */@MethodInfo(author = "七郎", comments = "方法測試注釋:print10", date = "2013-11-29", version = 1.0)public void print10(Object msg) {System.out.println(msg);}/** * 靜態方法 */@MethodInfo(author = "七郎", comments = "方法測試注釋:print20", date = "2013-11-20", version = 2.0)public static void print20() {System.out.println("test");}}
最後就是註解解析類(簡單起見我將代碼寫在了main方法裡):
package com.array7.annotationTest;import java.lang.annotation.Annotation;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;public class AnnotationParsing {public static void main(String[] args) {try {Class<?> clazz = Class.forName("com.array7.annotationTest.AnnotationExample");// 這裡可以封裝成動態代理類。// Class clazz = AnnotationParsing.class.getClassLoader().loadClass("com.array7.annotationTest.AnnotationExample");// 這種方式也OK。for (Method method : clazz.getMethods()) {if (method.isAnnotationPresent(MethodInfo.class)) {for (Annotation anno : method.getAnnotations()) {System.out.println("Annotation in Method [" + method + "]:" + anno);MethodInfo methodInfo = method.getAnnotation(MethodInfo.class);if (methodInfo.version() == 1.0) {method.invoke(clazz.newInstance(), methodInfo.comments());} else if (methodInfo.version() == 2.0) {method.invoke(null);}}}}} catch (SecurityException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (IllegalArgumentException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();} catch (InstantiationException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}
程式最後輸出:
Annotation in Method [public void com.array7.annotationTest.AnnotationExample.print10(java.lang.Object)]:@com.array7.annotationTest.MethodInfo(author=七郎, version=1.0, comments=方法測試注釋, date=2013-11-20)方法測試注釋Annotation in Method [public static void com.array7.annotationTest.AnnotationExample.print20()]:@com.array7.annotationTest.MethodInfo(author=七郎, version=2.0, comments=方法測試注釋, date=2013-11-20)test
通過上述輸出我們也可以發現,註解有限執行的是執行個體化之後的方法,然後才是靜態方法。
上面所提到的知識理論和實踐,是本人通過書籍、Java文檔、互連網資料總結並親自驗證的。
如果哪位道友發現有表達不準確的地方,歡迎指正,免得誤人誤己,多謝。