注解基礎篇:自定義Java Annotation

寫在前面


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í)行了!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容