<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    無(wú)為

    無(wú)為則可為,無(wú)為則至深!

      BlogJava :: 首頁(yè) :: 聯(lián)系 :: 聚合  :: 管理
      190 Posts :: 291 Stories :: 258 Comments :: 0 Trackbacks
    作者:cleverpig(作者的Blog:http://blog.matrix.org.cn/page/cleverpig)
    原文:http://www.matrix.org.cn/resource/article/44/44055_Java+Annotation+Reflect.html
    關(guān)鍵字:java,annotation,reflect

    前言:
    在上篇文章《Java Annotation入門(mén)》中概要性的介紹了Annotation的定義、使用,范圍涵蓋較廣,但是深度不夠。所以作者在《Java Annotation入門(mén)》后,繼續(xù)整理了Annotation的概念和知識(shí)點(diǎn),與喜歡research的朋友們共享。

    閱讀提示:文中提到的程序成員或者程序元素是一個(gè)概念,指組成程序代碼的單元:如類(lèi)、方法、成員變量。

    一、Annotation究竟是什么?

    Annotation 提供了一條與程序元素關(guān)聯(lián)任何信息或者任何元數(shù)據(jù)(metadata)的途徑。從某些方面看,annotation就像修飾符一樣被使用,并應(yīng)用于包、類(lèi) 型、構(gòu)造方法、方法、成員變量、參數(shù)、本地變量的聲明中。這些信息被存儲(chǔ)在annotation的“name=value”結(jié)構(gòu)對(duì)中。 annotation類(lèi)型是一種接口,能夠通過(guò)java反射API的方式提供對(duì)其信息的訪問(wèn)。

    annotation能被用來(lái)為某個(gè)程序元 素(類(lèi)、方法、成員變量等)關(guān)聯(lián)任何的信息。需要注意的是,這里存在著一個(gè)基本的潛規(guī)則:annotaion不能影響程序代碼的執(zhí)行,無(wú)論增加、刪除 annotation,代碼都始終如一的執(zhí)行。另外,盡管一些annotation通過(guò)java的反射api方法在運(yùn)行時(shí)被訪問(wèn),而java語(yǔ)言解釋器在 工作時(shí)忽略了這些annotation。正是由于java虛擬機(jī)忽略了annotation,導(dǎo)致了annotation類(lèi)型在代碼中是“不起作用”的; 只有通過(guò)某種配套的工具才會(huì)對(duì)annotation類(lèi)型中的信息進(jìn)行訪問(wèn)和處理。本文中將涵蓋標(biāo)準(zhǔn)的annotation和meta- annotation類(lèi)型,陪伴這些annotation類(lèi)型的工具是java編譯器(當(dāng)然要以某種特殊的方式處理它們)。

    由于上述原 因,annotation在使用時(shí)十分簡(jiǎn)便。一個(gè)本地變量可以被一個(gè)以NonNull命名的annotation類(lèi)型所標(biāo)注,來(lái)作為對(duì)這個(gè)本地變量不能被 賦予null值的斷言。而我們可以編寫(xiě)與之配套的一個(gè)annotation代碼分析工具,使用它來(lái)對(duì)具有前面變量的代碼進(jìn)行解析,并且嘗試驗(yàn)證這個(gè)斷言。 當(dāng)然這些代碼并不必自己編寫(xiě)。在JDK安裝后,在JDK/bin目錄中可以找到名為“apt”的工具,它提供了處理annotation的框架:它啟動(dòng)后 掃描源代碼中的annotation,并調(diào)用我們定義好的annotation處理器完成我們所要完成的工作(比如驗(yàn)證前面例子中的斷言)。說(shuō)到這里, annotation的強(qiáng)大功能似乎可以替代XDoclet這類(lèi)的工具了,隨著我們的深入,大家會(huì)更加堅(jiān)信這一點(diǎn)。
    注:詳細(xì)描述請(qǐng)參看jsr250規(guī)范:
    http://www.jcp.org/aboutJava/communityprocess/pfd/jsr250/

    二、Annotation的定義:

    這 段文字開(kāi)始介紹annotation相關(guān)技術(shù)。在此大家將看到j(luò)ava5.0的標(biāo)準(zhǔn)annotation類(lèi)型,這種標(biāo)準(zhǔn)類(lèi)型就是前文中所說(shuō)的“內(nèi)建”類(lèi) 型,它們可以直接被javac支持。可喜的是,在java6.0beta版中的javac已經(jīng)加入了對(duì)自定義annotation的支持。

    1。Annotation的概念和語(yǔ)法:

    首先,關(guān)鍵的概念是理解annotation是與一個(gè)程序元素相關(guān)聯(lián)信息或者元數(shù)據(jù)的標(biāo)注。它從不影響java程序的執(zhí)行,但是對(duì)例如編譯器警告或者像文檔生成器等輔助工具產(chǎn)生影響。

    下面是常用的annotation列表,我們應(yīng)該注意在annotation和annotation類(lèi)型之間的不同:

    A.annotation:
    annotation 使用了在java5.0所帶來(lái)的新語(yǔ)法,它的行為十分類(lèi)似public、final這樣的修飾符。每個(gè)annotation具有一個(gè)名字和成員個(gè)數(shù) >=0。每個(gè)annotation的成員具有被稱為name=value對(duì)的名字和值(就像javabean一樣),name=value裝載了 annotation的信息。

    B.annotation類(lèi)型:
    annotation 類(lèi)型定義了annotation的名字、類(lèi)型、成員默認(rèn)值。一個(gè)annotation類(lèi)型可以說(shuō)是一個(gè)特殊的java接口,它的成員變量是受限制的,而聲 明annotation類(lèi)型時(shí)需要使用新語(yǔ)法。當(dāng)我們通過(guò)java反射api訪問(wèn)annotation時(shí),返回值將是一個(gè)實(shí)現(xiàn)了該annotation類(lèi) 型接口的對(duì)象,通過(guò)訪問(wèn)這個(gè)對(duì)象我們能方便的訪問(wèn)到其annotation成員。后面的章節(jié)將提到在java5.0的java.lang包里包含的3個(gè)標(biāo) 準(zhǔn)annotation類(lèi)型。

    C.annotation成員:
    annotation 的成員在annotation類(lèi)型中以無(wú)參數(shù)的方法的形式被聲明。其方法名和返回值定義了該成員的名字和類(lèi)型。在此有一個(gè)特定的默認(rèn)語(yǔ)法:允許聲明任何 annotation成員的默認(rèn)值:一個(gè)annotation可以將name=value對(duì)作為沒(méi)有定義默認(rèn)值的annotation成員的值,當(dāng)然也可 以使用name=value對(duì)來(lái)覆蓋其它成員默認(rèn)值。這一點(diǎn)有些近似類(lèi)的繼承特性,父類(lèi)的構(gòu)造函數(shù)可以作為子類(lèi)的默認(rèn)構(gòu)造函數(shù),但是也可以被子類(lèi)覆蓋。

    D.marker annotation類(lèi)型:
    一個(gè)沒(méi)有成員定義的annotation類(lèi)型被稱為marker annotation。這種annotation類(lèi)型僅使用自身的存在與否來(lái)為我們提供信息。如后面要說(shuō)的Override。

    E.meta-annotation:
    meta -annotation也稱為元annotation,它是被用來(lái)聲明annotation類(lèi)型的annotation。Java5.0提供了一些標(biāo)準(zhǔn)的 元-annotation類(lèi)型。下面介紹的target、retention就是meta-annotation。

    F.target:
    annotation 的target是一個(gè)被標(biāo)注的程序元素。target說(shuō)明了annotation所修飾的對(duì)象范圍:annotation可被用于packages、 types(類(lèi)、接口、枚舉、annotation類(lèi)型)、類(lèi)型成員(方法、構(gòu)造方法、成員變量、枚舉值)、方法參數(shù)和本地變量(如循環(huán)變量、catch 參數(shù))。在annotation類(lèi)型的聲明中使用了target可更加明晰其修飾的目標(biāo)。

    G.retention:
    annotation 的retention定義了該annotation被保留的時(shí)間長(zhǎng)短:某些annotation僅出現(xiàn)在源代碼中,而被編譯器丟棄;而另一些卻被編譯在 class文件中;編譯在class文件中的annotation可能會(huì)被虛擬機(jī)忽略,而另一些在class被裝載時(shí)將被讀取(請(qǐng)注意并不影響class 的執(zhí)行,因?yàn)閍nnotation與class在使用上是被分離的)。使用這個(gè)meta-annotation可以對(duì)annotation的“生命周期” 限制。

    H.metadata:
    由于metadata被廣泛使用于各種計(jì)算機(jī)開(kāi)發(fā)過(guò)程中,所以當(dāng)我們?cè)谶@里談?wù)摰膍etadata即元數(shù)據(jù)通常指被annotation裝載的信息或者annotation本身。

    2。使用標(biāo)準(zhǔn)Annotation:
    java5.0在java.lang包中定義了3種標(biāo)準(zhǔn)的annotation類(lèi)型:

    A.Override:
    java.lang.Override 是一個(gè)marker annotation類(lèi)型,它被用作標(biāo)注方法。它說(shuō)明了被標(biāo)注的方法重載了父類(lèi)的方法,起到了斷言的作用。如果我們使用了這種annotation在一個(gè) 沒(méi)有覆蓋父類(lèi)方法的方法時(shí),java編譯器將以一個(gè)編譯錯(cuò)誤來(lái)警示。
    這個(gè)annotaton常常在我們?cè)噲D覆蓋父類(lèi)方法而確又寫(xiě)錯(cuò)了方法名時(shí)發(fā)揮威力。

    使用方法極其簡(jiǎn)單:在使用此annotation時(shí)只要在被修飾的方法前面加上@Override。
    下面的代碼是一個(gè)使用@Override修飾一個(gè)企圖重載父類(lèi)的toString方法,而又存在拼寫(xiě)錯(cuò)誤的sample:
    清單1:

    @Override
    public String toSting() {   // 注意方法名拼寫(xiě)錯(cuò)了
        return "[" + super.toString() + "]";
    }


    B.Deprecated:
    同 樣Deprecated也是一個(gè)marker annotation。當(dāng)一個(gè)類(lèi)型或者類(lèi)型成員使用@Deprecated修飾的話,編譯器將不鼓勵(lì)使用這個(gè)被標(biāo)注的程序元素。而且這種修飾具有一定的 “延續(xù)性”:如果我們?cè)诖a中通過(guò)繼承或者覆蓋的方式使用了這個(gè)過(guò)時(shí)的類(lèi)型或者成員,雖然繼承或者覆蓋后的類(lèi)型或者成員并不是被聲明為 @Deprecated,但編譯器仍然要報(bào)警。
    值得注意,@Deprecated這個(gè)annotation類(lèi)型和javadoc中的 @deprecated這個(gè)tag是有區(qū)別的:前者是java編譯器識(shí)別的,而后者是被javadoc工具所識(shí)別用來(lái)生成文檔(包含程序成員為什么已經(jīng)過(guò) 時(shí)、它應(yīng)當(dāng)如何被禁止或者替代的描述)。
    在java5.0,java編譯器仍然象其從前版本那樣尋找@deprecated這個(gè)javadoc tag,并使用它們產(chǎn)生警告信息。但是這種狀況將在后續(xù)版本中改變,我們應(yīng)在現(xiàn)在就開(kāi)始使用@Deprecated來(lái)修飾過(guò)時(shí)的方法而不是 @deprecated javadoc tag。
    清單2:

    下面是一段使用@Deprecated的代碼:
    /**
    * 這里是javadoc的@deprecated聲明.
    * @deprecated No one has players for this format any more.  Use VHS instead.
    */
    @Deprecated public class Betamax { ... }


    C.SuppressWarnings:
    @SuppressWarnings 被用于有選擇的關(guān)閉編譯器對(duì)類(lèi)、方法、成員變量、變量初始化的警告。在java5.0,sun提供的javac編譯器為我們提供了-Xlint選項(xiàng)來(lái)使編 譯器對(duì)合法的程序代碼提出警告,此種警告從某種程度上代表了程序錯(cuò)誤。例如當(dāng)我們使用一個(gè)generic collection類(lèi)而又沒(méi)有提供它的類(lèi)型時(shí),編譯器將提示出"unchecked warning"的警告。

    通常當(dāng)這種情況發(fā)生時(shí),我們就需要查找引起警告的代碼。如果它真的表示錯(cuò)誤,我們就需要糾正它。例如如果警告信息表明我們代碼中的switch語(yǔ)句沒(méi)有覆蓋所有可能的case,那么我們就應(yīng)增加一個(gè)默認(rèn)的case來(lái)避免這種警告。
    相 仿,有時(shí)我們無(wú)法避免這種警告,例如,我們使用必須和非generic的舊代碼交互的generic collection類(lèi)時(shí),我們不能避免這個(gè)unchecked warning。此時(shí)@SuppressWarning就要派上用場(chǎng)了,在調(diào)用的方法前增加@SuppressWarnings修飾,告訴編譯器停止對(duì)此 方法的警告。
    SuppressWarning不是一個(gè)marker annotation。它有一個(gè)類(lèi)型為String[]的成員,這個(gè)成員的值為被禁止的警告名。對(duì)于javac編譯器來(lái)講,被-Xlint選項(xiàng)有效的警告 名也同樣對(duì)@SuppressWarings有效,同時(shí)編譯器忽略掉無(wú)法識(shí)別的警告名。

    annotation語(yǔ)法允許在annotation名后跟括號(hào),括號(hào)中是使用逗號(hào)分割的name=value對(duì)用于為annotation的成員賦值:
    清單3:

    @SuppressWarnings(value={"unchecked","fallthrough"})
    public void lintTrap() { /* sloppy method body omitted */ }


    在這個(gè)例子中SuppressWarnings annotation類(lèi)型只定義了一個(gè)單一的成員,所以只有一個(gè)簡(jiǎn)單的value={...}作為name=value對(duì)。又由于成員值是一個(gè)數(shù)組,故使用大括號(hào)來(lái)聲明數(shù)組值。

    注意:我們可以在下面的情況中縮寫(xiě)annotation:當(dāng)annotation只有單一成員,并成員命名為"value="。這時(shí)可以省去"value="。比如將上面的SuppressWarnings annotation進(jìn)行縮寫(xiě):
    清單4:

    @SuppressWarnings({"unchecked","fallthrough"})

    如果SuppressWarnings所聲明的被禁止警告?zhèn)€數(shù)為一個(gè)時(shí),可以省去大括號(hào):

    @SuppressWarnings("unchecked")


    3。Annotation語(yǔ)法:

    在上一個(gè)章節(jié)中,我們看到書(shū)寫(xiě)marker annotation和單一成員annotation的語(yǔ)法。下面本人來(lái)介紹一下完整的語(yǔ)法:

    annotation 由“@+annotation類(lèi)型名稱+(..逗號(hào)分割的name-value對(duì)...)”組成。其中成員可以按照任何的順序。如果annotation 類(lèi)型定義了某個(gè)成員的默認(rèn)值,則這個(gè)成員可以被省略。成員值必須為編譯時(shí)常量、內(nèi)嵌的annotation或者數(shù)組。

    下面我們將定義一個(gè) annotation類(lèi)型名為Reviews,它有一個(gè)由@Review annotation數(shù)組構(gòu)成的成員。這個(gè)@Review annotation類(lèi)型有三個(gè)成員:"reviewer"是一個(gè)字符串,"comment" 是一個(gè)具有默認(rèn)值的可選的字符串,"grade"是一個(gè)Review.Grade枚舉類(lèi)型值。
    清單5:

    @Reviews({  // Single-value annotation, so "value=" is omitted here
        @Review(grade=Review.Grade.EXCELLENT,
                reviewer="df"),
        @Review(grade=Review.Grade.UNSATISFACTORY,
                reviewer="eg",
                comment="This method needs an @Override annotation")
    })

    annotation語(yǔ)法的另一個(gè)重要規(guī)則是沒(méi)有程序成員可以有多于一個(gè)的同一annotation實(shí)例。例如在一個(gè)類(lèi)中簡(jiǎn)單的放置多個(gè)@Review annotation。這也是在上面代碼中定義@Reviews annotation類(lèi)型數(shù)組的原因。

    4。Annotation成員類(lèi)型和值:

    annotation成員必須是非空的編譯時(shí)常量表達(dá)式。可用的成員類(lèi)型為:primitive類(lèi)型、, String, Class, enumerated類(lèi)型, annotation類(lèi)型, 和前面類(lèi)型的數(shù)組。

    下面我們定義了一個(gè)名為UncheckedExceptions 的annotation類(lèi)型,它的成員是一個(gè)擴(kuò)展了RuntimeException類(lèi)的類(lèi)數(shù)組。
    清單6:

    @UncheckedExceptions({
        IllegalArgumentException.class, StringIndexOutOfBoundsException.class
    })


    5。Annotation的目標(biāo):

    annotation通常被放在類(lèi)型定義和成員定義的前面。然而它也出現(xiàn)在package、方法參數(shù)、本地變量的前面。下面,我們來(lái)討論一下這些不大常用的寫(xiě)法:

    package annotation出現(xiàn)在package聲明的前面。
    下面的例子package-info.java中不包含任何的公共類(lèi)型定義,卻包含一個(gè)可選的javadoc注釋。
    清單7:

    /**
    * This package holds my custom annotation types.
    */
    @com.davidflanagan.annotations.Author("David Flanagan")
    package com.davidflanagan.annotations;

    當(dāng)package -info.java文件被編譯時(shí),它將產(chǎn)生名為包含annotation(特殊的接口)聲明的package-info.class的類(lèi)。這個(gè)接口沒(méi)有 成員,它的名字package-info不是一個(gè)合法的java標(biāo)識(shí),所以它不能用在java源代碼中。這個(gè)接口的存在只是簡(jiǎn)單的被看作一個(gè)為 package annotation準(zhǔn)備的占位符。

    用于修飾方法參數(shù)、catch參數(shù)、本地變量的annotation只是簡(jiǎn)單的出現(xiàn) 在這些程序成員的修飾符位置。java類(lèi)文件格式?jīng)]有為本地變量或者catch參數(shù)存儲(chǔ)annotation作準(zhǔn)備,所以這些annotation總是保 留在源代碼級(jí)別(source retention);方法參數(shù)annotation能夠保存在類(lèi)文件中,也可以在保留到運(yùn)行時(shí)。

    最后,請(qǐng)注意,枚舉類(lèi)型定義中不允許任何的修飾符修飾其枚舉值。

    6。Annotation和默認(rèn)值:
    在Annotation 中,沒(méi)有默認(rèn)值的成員必須有一個(gè)成員值。而如何理解默認(rèn)值是如何被處理就是一個(gè)很重要的細(xì)節(jié):annotation類(lèi)型所定義的成員默認(rèn)值被存儲(chǔ)在 class文件中,不被編譯到annotation里面。如果我們修改一個(gè)annotation類(lèi)型使其成員的默認(rèn)值發(fā)生了改變,這個(gè)改變對(duì)于所有此類(lèi)型 的annotation中沒(méi)有明確提供成員值的成員產(chǎn)生影響(即修改了該成員的成員值)。即使在annotation類(lèi)型使其成員的默認(rèn)值被改變后 annotation從沒(méi)被重新編譯過(guò),該類(lèi)型的annotation(改變前已經(jīng)被編譯的)也受到影響。

    三、Annotation工作原理:

    Annotation與反射
    在java5.0 中Java.lang.reflect提供的反射API被擴(kuò)充了讀取運(yùn)行時(shí)annotation的能力。讓我們回顧一下前面所講的:一個(gè) annotation類(lèi)型被定義為runtime retention后,它才是在運(yùn)行時(shí)可見(jiàn),當(dāng)class文件被裝載時(shí)被保存在class文件中的annotation才會(huì)被虛擬機(jī)讀取。那么 reflect是如何幫助我們?cè)L問(wèn)class中的annotation呢?

    下文將在java.lang.reflect用于 annotation的新特性,其中java.lang.reflect.AnnotatedElement是重要的接口,它代表了提供查詢 annotation能力的程序成員。這個(gè)接口被java.lang.Package、java.lang.Class實(shí)現(xiàn),并間接地被Method類(lèi)、 Constructor類(lèi)、java.lang.reflect的Field類(lèi)實(shí)現(xiàn)。而annotation中的方法參數(shù)可以通過(guò)Method類(lèi)、 Constructor類(lèi)的getParameterAnnotations()方法獲得。

    下面的代碼使用了AnnotatedElement類(lèi)的isAnnotationPresent()方法判斷某個(gè)方法是否具有@Unstable annotation,從而斷言此方法是否穩(wěn)定:
    清單8:

    import java.lang.reflect.*;

    Class c = WhizzBangClass.class;                          
    Method m = c.getMethod("whizzy", int.class, int.class);  
    boolean unstable = m.isAnnotationPresent(Unstable.class);

    isAnnotationPresent ()方法對(duì)于檢查marker annotation是十分有用的,因?yàn)閙arker annotation沒(méi)有成員變量,所以我們只要知道class的方法是否使用了annotation修飾就可以了。而當(dāng)處理具有成員的 annotation時(shí),我們通過(guò)使用getAnnotation()方法來(lái)獲得annotation的成員信息(成員名稱、成員值)。這里我們看到了一 套優(yōu)美的java annotation系統(tǒng):如果annotation存在,那么實(shí)現(xiàn)了相應(yīng)的annotation類(lèi)型接口的對(duì)象將被getAnnotation()方法 返回,接著調(diào)用定義在annotation類(lèi)型中的成員方法可以方便地獲得任何成員值。

    回想一下,前面介紹的@Reviews annotation,如果這個(gè)annotation類(lèi)型被聲明為runtime retention的話,我們通過(guò)下面的代碼來(lái)訪問(wèn)@Reviews annotation的成員值:
    清單9:

    AnnotatedElement target = WhizzBangClass.class; //獲得被查詢的AnnotatedElement
    // 查詢AnnotatedElement的@Reviews annotation信息
    Reviews annotation = target.getAnnotation(Reviews.class);
    // 因?yàn)?#64;Reviews annotation類(lèi)型的成員為@Review annotation類(lèi)型的數(shù)組,
    // 所以下面聲明了Review[] reviews保存@Reviews annotation類(lèi)型的value成員值。
    Review[] reviews = annotation.value();
    // 查詢每個(gè)@Review annotation的成員信息
    for(Review r : reviews) {
        Review.Grade grade = r.grade();
        String reviewer = r.reviewer();
        String comment = r.comment();
        System.out.printf("%s assigned a grade of %s and comment '%s'%n",
                          reviewer, grade, comment);
    }


    四、如何自定義Annotation?

    1.詳解annotation與接口的異同:
    因?yàn)閍nnotation類(lèi)型是一個(gè)非凡的接口,所以兩者之間存在著某些差異:

    A.Annotation類(lèi)型使用關(guān)鍵字@interface而不是interface。
    這個(gè)關(guān)鍵字聲明隱含了一個(gè)信息:它是繼承了java.lang.annotation.Annotation接口,并非聲明了一個(gè)interface。

    B.Annotation類(lèi)型、方法定義是獨(dú)特的、受限制的。
    Annotation 類(lèi)型的方法必須聲明為無(wú)參數(shù)、無(wú)異常拋出的。這些方法定義了annotation的成員:方法名成為了成員名,而方法返回值成為了成員的類(lèi)型。而方法返回 值類(lèi)型必須為primitive類(lèi)型、Class類(lèi)型、枚舉類(lèi)型、annotation類(lèi)型或者由前面類(lèi)型之一作為元素的一維數(shù)組。方法的后面可以使用 default和一個(gè)默認(rèn)數(shù)值來(lái)聲明成員的默認(rèn)值,null不能作為成員默認(rèn)值,這與我們?cè)诜莂nnotation類(lèi)型中定義方法有很大不同。
    Annotation類(lèi)型和它的方法不能使用annotation類(lèi)型的參數(shù)、成員不能是generic。只有返回值類(lèi)型是Class的方法可以在annotation類(lèi)型中使用generic,因?yàn)榇朔椒軌蛴妙?lèi)轉(zhuǎn)換將各種類(lèi)型轉(zhuǎn)換為Class。

    C.Annotation類(lèi)型又與接口有著近似之處。
    它們可以定義常量、靜態(tài)成員類(lèi)型(比如枚舉類(lèi)型定義)。Annotation類(lèi)型也可以如接口一般被實(shí)現(xiàn)或者繼承。

    2.實(shí)例:
    下面,我們將看到如何定義annotation類(lèi)型的example。它展示了annotation類(lèi)型聲明以及@interface與interface之間的不同:
    清單10:

    package com.davidflanagan.annotations;
    import java.lang.annotation.*;

    /**
    * 使用annotation來(lái)描述那些被標(biāo)注的成員是不穩(wěn)定的,需要更改
    */
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Unstable {}


    下面的另一個(gè)example只定義了一個(gè)成員。并通過(guò)將這個(gè)成員命名為value,使我們可以方便的使用這種annotation的快捷聲明方式:
    清單11:

    /**
    * 使用Author這個(gè)annotation定義在程序中指出代碼的作者
    */
    public @interface Author {
        /** 返回作者名 */
        String value();
    }


    以 下的example更加復(fù)雜。Reviews annotation類(lèi)型只有一個(gè)成員,但是這個(gè)成員的類(lèi)型是復(fù)雜的:由Review annotation組成的數(shù)組。Review annotation類(lèi)型有3個(gè)成員:枚舉類(lèi)型成員grade、表示Review名稱的字符串類(lèi)型成員Reviewer、具有默認(rèn)值的字符串類(lèi)型成員 Comment。
    清單12:

    import java.lang.annotation.*;
            
    /**
    * Reviews annotation類(lèi)型只有一個(gè)成員,
    * 但是這個(gè)成員的類(lèi)型是復(fù)雜的:由Review annotation組成的數(shù)組
    */
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Reviews {
        Review[] value();
    }

    /**
    * Review annotation類(lèi)型有3個(gè)成員:
    * 枚舉類(lèi)型成員grade、
      * 表示Review名稱的字符串類(lèi)型成員Reviewer、
      * 具有默認(rèn)值的字符串類(lèi)型成員Comment。
    */
    public @interface Review {
        // 內(nèi)嵌的枚舉類(lèi)型
        public static enum Grade { EXCELLENT, SATISFACTORY, UNSATISFACTORY };

        // 下面的方法定義了annotation的成員
        Grade grade();                
        String reviewer();          
        String comment() default "";  
    }


    最 后,我們來(lái)定義一個(gè)annotation方法用于羅列出類(lèi)運(yùn)行中所有的unchecked異常(上文已經(jīng)提到這種情況不一定是錯(cuò)誤)。這個(gè) annotation類(lèi)型將一個(gè)數(shù)組作為了唯一的成員。數(shù)組中的每個(gè)元素都是異常類(lèi)。為了加強(qiáng)對(duì)未檢查的異常(此類(lèi)異常都是在運(yùn)行時(shí)拋出)進(jìn)行報(bào)告,我們 可以在代碼中對(duì)異常的類(lèi)型進(jìn)行限制:
    清單13:

    public @interface UncheckedExceptions {
        Class<? extends RuntimeException>[] value();
    }


    五、Meta-Annotation

    Annotation 類(lèi)型可以被它們自己所標(biāo)注。Java5.0定義了4個(gè)標(biāo)準(zhǔn)的meta-annotation類(lèi)型,它們被用來(lái)提供對(duì)其它annotation類(lèi)型作說(shuō)明。 這些類(lèi)型和它們所支持的類(lèi)在java.lang.annotation包中可以找到。如果需要更詳細(xì)的信息可以參考jdk5.0手冊(cè)。

    1.再談Target
    作 為meta-annotation類(lèi)型的Target,它描述了annotation所修飾的程序成員的類(lèi)型。當(dāng)一個(gè)annotation類(lèi)型沒(méi)有 Target時(shí),它將被作為普通的annotation看待。當(dāng)將它修飾一個(gè)特定的程序成員時(shí),它將發(fā)揮其應(yīng)用的作用,例如:Override用于修飾方 法時(shí),增加了@Target這個(gè)meta-annotation就使編譯器對(duì)annotation作檢查,從而去掉修飾錯(cuò)誤類(lèi)型的Override。

    Target meta-annotation類(lèi)型有唯一的value作為成員。這個(gè)成員的類(lèi)型是java.lang.annotation.ElementType[]類(lèi)型的,ElementType類(lèi)型是可以被標(biāo)注的程序成員的枚舉類(lèi)型。

    2.Retention的用法
    我 們?cè)谖恼碌拈_(kāi)頭曾經(jīng)提到過(guò)Retention,但是沒(méi)有詳細(xì)講解。Retention描述了annotation是否被編譯器丟棄或者保留在class文 件;如果保留在class文件中,是否在class文件被裝載時(shí)被虛擬機(jī)讀取。默認(rèn)情況下,annotation被保存在class文件中,但在運(yùn)行時(shí)并 不能被反射訪問(wèn)。Retention具有三個(gè)取值:source、class、runtime,這些取值來(lái)自 java.lang.annotation.RetentionPolicy的枚舉類(lèi)型值。

    Retention meta-annotation類(lèi)型有唯一的value作為成員,它的取值來(lái)自java.lang.annotation.RetentionPolicy的枚舉類(lèi)型值。

    3.Documented
    Documented是一個(gè)meta-annotation類(lèi)型,用于描述其它類(lèi)型的annotation應(yīng)該被作為被標(biāo)注的程序成員的公共API,因此可以被例如javadoc此類(lèi)的工具文檔化。

    Documented是一個(gè)marker annotation,沒(méi)有成員。

    4.Inherited
    @Inherited meta-annotation也是一個(gè)marker annotation,它闡述了某個(gè)被標(biāo)注的類(lèi)型是被繼承的。如果一個(gè)使用了@Inherited修飾的annotation類(lèi)型被用于一個(gè)class, 則這個(gè)annotation將被用于該class的子類(lèi)。

    注意:@Inherited annotation類(lèi)型是被標(biāo)注過(guò)的class的子類(lèi)所繼承。類(lèi)并不從它所實(shí)現(xiàn)的接口繼承annotation,方法并不從它所重載的方法繼承annotation。

    值 得思考的是,當(dāng)@Inherited annotation類(lèi)型標(biāo)注的annotation的Retention是RetentionPolicy.RUNTIME,則反射API增強(qiáng)了這種繼 承性。如果我們使用java.lang.reflect去查詢一個(gè)@Inherited annotation類(lèi)型的annotation時(shí),反射代碼檢查將展開(kāi)工作:檢查class和其父類(lèi),直到發(fā)現(xiàn)指定的annotation類(lèi)型被發(fā)現(xiàn), 或者到達(dá)類(lèi)繼承結(jié)構(gòu)的頂層。

    六、總結(jié):

    本文幾乎 覆蓋了所有的Annotation的概念和知識(shí)點(diǎn),從annotation的定義、語(yǔ)法到工作原理、如何自定義annotation,直至meta- annotation。其中也具有一些配套的代碼片斷可參考,雖然不是很多,但是可謂言簡(jiǎn)意賅、著其重點(diǎn),本人認(rèn)為用好annotation的關(guān)鍵還在于 使用。希望本手冊(cè)能夠幫助大家用好annotation,這也是本人的最大快樂(lè)。


    凡是有該標(biāo)志的文章,都是該blog博主Caoer(草兒)原創(chuàng),凡是索引、收藏
    、轉(zhuǎn)載請(qǐng)注明來(lái)處和原文作者。非常感謝。

    posted on 2007-09-06 19:11 草兒 閱讀(60258) 評(píng)論(15)  編輯  收藏 所屬分類(lèi): java

    Feedback

    # re: Java Annotation手冊(cè) 2007-09-10 00:14 阿牛,專注OOP
    也是就.NET中的元數(shù)據(jù),這是$MS發(fā)明的,現(xiàn)在Java也開(kāi)始抄.NET的東西了!  回復(fù)  更多評(píng)論
      

    # re: Java Annotation手冊(cè) 2007-11-13 09:44 楊一
    Marker Interface, Marker Annotation. The introduce of this function has violated the simple rule of "Java's Simple". However it does enhanced the language, especially in the field of Web Services  回復(fù)  更多評(píng)論
      

    # re: Java Annotation手冊(cè) 2007-12-29 17:45 gembin
    good  回復(fù)  更多評(píng)論
      

    # re: Java Annotation手冊(cè) 2008-02-18 20:05 littlemice
    此文啟蒙我走進(jìn)annotation的世界,非常感謝!至此,我想再次看tkij4的annotation部分定將輕車(chē)熟路  回復(fù)  更多評(píng)論
      

    # re: Java Annotation手冊(cè) 2008-03-10 11:31 Kill
    元數(shù)據(jù)并非MS發(fā)明,只能說(shuō)它比JAVA早加入了這個(gè)特性。  回復(fù)  更多評(píng)論
      

    # re: Java Annotation手冊(cè) 2008-03-12 16:14 阿布操
    THANKS  回復(fù)  更多評(píng)論
      

    # 展開(kāi)一下@Inherited 2008-04-19 11:08 jactive
    /* created by jactive on Apr 19, 2008 10:47:56 AM */

    package mydemo;

    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;


    /**
    * <p>
    * @author jactive
    * @version 1.0
    * @see
    * @since 1.0
    */
    public class Demo5 {
    public static void main(String[] args) {
    try {
    Demo1.visit(Demo6.class);
    Demo1.visit(Demo7.class);

    Class<?>[] paramTypes = new Class[0];
    Demo1.visit(Demo6.class.getMethod("mtd", paramTypes));
    Demo1.visit(Demo7.class.getMethod("mtd", paramTypes));
    Demo1.visit(Demo8.class.getMethod("mtd", paramTypes));
    Demo1.visit(Demo9.class.getMethod("mtd", paramTypes));

    } catch (Exception e) {
    e.printStackTrace();
    }


    }
    }


    class Demo6 implements MyInterface {
    public void mtd() {
    System.out.println("Demo6#mtd method is called");

    }
    }

    class Demo7 extends MySuperClass {

    public void mtd() {
    System.out.println("Demo7#mtd method is called");

    }
    }

    class Demo8 extends MySuperClass2 {

    }

    class Demo9 extends MySuperClass2 {
    public void mtd() { }
    }

    @UdtAnnotation
    interface MyInterface {
    @UdtAnnotation("該方法必須被實(shí)現(xiàn),因此當(dāng)前注解實(shí)例不會(huì)被繼承")
    public void mtd();
    }

    @UdtAnnotation
    abstract class MySuperClass {
    @UdtAnnotation("該方法必須被覆蓋,因此當(dāng)前注解實(shí)例不會(huì)被繼承")
    public abstract void mtd();
    }

    @UdtAnnotation
    abstract class MySuperClass2 {
    @UdtAnnotation("覆蓋override該方法后不能繼承注解,只有繼承的方法才能繼承注解")
    public void mtd() { }
    }


    @Inherited
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE, ElementType.METHOD})
    @interface UdtAnnotation {
    String value() default "default value";
    }

      回復(fù)  更多評(píng)論
      

    # 展開(kāi)一下@Inherited 2008-04-19 11:09 jactive
    /* created by jactive on Apr 13, 2008 10:22:19 PM */

    package mydemo;

    import java.lang.annotation.Annotation;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    import java.lang.reflect.AnnotatedElement;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;

    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;

    /**
    * <p>反射使用Annotation
    * @author jactive
    * @version 1.0
    * @see
    * @since 1.0
    */
    @Demo1.Reviews(id=10, grade=Demo1.Reviews.Grade.Excellent, value="^^^")
    public class Demo1 {
    private static Log log = LogFactory.getLog(Demo1.class);

    // 默認(rèn)是RetentionPolicy.Class,只能在編譯期使用,在runtime無(wú)法通過(guò)反射獲得
    @Retention(RetentionPolicy.RUNTIME)
    // 默認(rèn)能修飾所有的ElementType中枚舉的所有內(nèi)容,這里讓他只能修飾類(lèi)型和方法
    @Target({ElementType.TYPE, ElementType.METHOD})
    public @interface Reviews {
    int KKK = 12; // public static final 類(lèi)似借口

    int id() default 1;
    Grade grade() default Grade.Passed;
    String value() default "";

    // 靜態(tài)成員類(lèi)型
    public enum Grade {
    Excellent, Passed, NotPassed,
    }
    }

    // 只能賦值給value屬性
    @Demo1.Reviews("123")
    class Demo2 {

    }

    @Reviews()
    public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
    Class<?> visitedAnnotatedElement = Demo1.class;
    visit(visitedAnnotatedElement);

    visitedAnnotatedElement = Demo2.class;
    visit(visitedAnnotatedElement);

    }

    public static void visit(AnnotatedElement target) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
    Annotation[] annotations = target.getAnnotations();
    if(null == annotations || 0 == annotations.length) {
    clearBuilder().append("no annotation used in ").append(target);
    log.debug(builder.toString());
    } else {
    clearBuilder().append("annotation used in ")
    .append(target);
    addCR();
    // annotation的方法的參數(shù),annotation的方法都是無(wú)參方法,有參方法由vm調(diào)用,在實(shí)例化annotation對(duì)象時(shí)使用
    Object[] params = new Object[0];
    for(Annotation annotation : annotations) {
    builder.append(annotation).append(":");
    addCR();
    Method[] annotationMethods = annotation.annotationType().getDeclaredMethods();
    for(Method annotationMethod : annotationMethods) {
    builder.append(annotationMethod.getName()).append(": ");
    Object ret = annotationMethod.invoke(annotation, params);
    builder.append(ret);
    addCR();
    }
    }
    log.debug(builder.toString());
    }
    }


    private static StringBuilder builder = new StringBuilder();
    private static StringBuilder clearBuilder() {
    return builder.delete(0, Integer.MAX_VALUE);
    }
    private static StringBuilder addCR() {
    return builder.append("\n");
    }
    }

    // eclipse中會(huì)有警告提示,但javac不會(huì)有
    class Demo3 implements Demo1.Reviews {

    public Grade grade() {
    System.out.println("Demo3#grade method is called");
    return null;
    }

    public int id() {
    System.out.println("Demo3#id method is called");
    return 0;
    }

    public String value() {
    System.out.println("Demo3#value method is called");
    return null;
    }

    public Class<? extends Annotation> annotationType() {
    System.out.println("Demo3#annotationType method is called");
    return null;
    }

    }

    @Demo1.Reviews()
    // eclipse中會(huì)有警告提示,但javac不會(huì)有
    interface Demo4 extends Demo1.Reviews { }


      回復(fù)  更多評(píng)論
      

    # re: Java Annotation手冊(cè)[未登錄](méi) 2008-09-24 13:38 nick
    @阿牛,專注OOP
    哈哈,元數(shù)據(jù)什么時(shí)候成了MS的發(fā)明了?  回復(fù)  更多評(píng)論
      

    # re: Java Annotation手冊(cè) 2008-12-04 14:05 javafuns
    不錯(cuò),寫(xiě)的不錯(cuò)  回復(fù)  更多評(píng)論
      

    # re: Java Annotation手冊(cè) 2009-03-30 13:42 舞命小丟
    好文章  回復(fù)  更多評(píng)論
      

    # re: Java Annotation手冊(cè) 2009-07-29 10:32 mo
    【這是$MS發(fā)明的,現(xiàn)在Java也開(kāi)始抄.NET的東西了】 無(wú)知!  回復(fù)  更多評(píng)論
      

    # re: Java Annotation手冊(cè) 2010-06-29 10:35 fuaiia
    無(wú)所謂 誰(shuí)抄襲誰(shuí)的 我們 本著拿來(lái)主義使用就對(duì)了   回復(fù)  更多評(píng)論
      

    # re: Java Annotation手冊(cè)[未登錄](méi) 2011-11-15 16:12 啊啊
    @阿牛,專注OOP
    好創(chuàng)意大家用,這就是開(kāi)源的思想,沒(méi)什么抄襲一說(shuō)的,也沒(méi)什么不好意思用的。  回復(fù)  更多評(píng)論
      

    # re: Java Annotation手冊(cè)[未登錄](méi) 2012-10-15 14:05 a
    翻譯的晦澀難懂  回復(fù)  更多評(píng)論
      

    主站蜘蛛池模板: 免费在线看v网址| 无码不卡亚洲成?人片| 五月婷婷综合免费| 亚洲成年轻人电影网站www| 亚洲综合激情五月色一区| 国产区在线免费观看| 最新免费jlzzjlzz在线播放| 亚洲国产精品无码久久一线| 日韩亚洲综合精品国产| 在线观看免费人成视频色9| 亚洲午夜国产精品无码| 久久亚洲精品无码网站| 国产成人精品免费视频网页大全| 国产亚洲情侣一区二区无| 含羞草国产亚洲精品岁国产精品| 国产精品国产免费无码专区不卡 | 视频免费1区二区三区| 毛色毛片免费观看| 91在线精品亚洲一区二区| 国产乱妇高清无乱码免费| 色噜噜亚洲精品中文字幕| 搜日本一区二区三区免费高清视频 | 成人免费无码精品国产电影| 亚洲精品视频在线播放| 两个人看的www免费视频| 免费中文字幕在线| 亚洲av无码一区二区三区天堂| va亚洲va日韩不卡在线观看| 成人一区二区免费视频| 亚洲伊人成无码综合网| 国产尤物在线视精品在亚洲| 最新猫咪www免费人成| 黄色视屏在线免费播放| 亚洲黄色在线观看| 国产网站在线免费观看| 可以免费观看的毛片| 国产成A人亚洲精V品无码性色 | 亚洲AV综合色区无码二区偷拍| 亚洲网站免费观看| 久久久久亚洲av无码专区喷水| 黄色网址在线免费|