無為

          無為則可為,無為則至深!

            BlogJava :: 首頁 :: 聯(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入門》中概要性的介紹了Annotation的定義、使用,范圍涵蓋較廣,但是深度不夠。所以作者在《Java Annotation入門》后,繼續(xù)整理了Annotation的概念和知識點(diǎn),與喜歡research的朋友們共享。

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

          一、Annotation究竟是什么?

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

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

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

          二、Annotation的定義:

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

          1。Annotation的概念和語法:

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

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

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

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

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

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

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

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

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

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

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

          A.Override:
          java.lang.Override 是一個marker annotation類型,它被用作標(biāo)注方法。它說明了被標(biāo)注的方法重載了父類的方法,起到了斷言的作用。如果我們使用了這種annotation在一個 沒有覆蓋父類方法的方法時,java編譯器將以一個編譯錯誤來警示。
          這個annotaton常常在我們試圖覆蓋父類方法而確又寫錯了方法名時發(fā)揮威力。

          使用方法極其簡單:在使用此annotation時只要在被修飾的方法前面加上@Override。
          下面的代碼是一個使用@Override修飾一個企圖重載父類的toString方法,而又存在拼寫錯誤的sample:
          清單1:

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


          B.Deprecated:
          同 樣Deprecated也是一個marker annotation。當(dāng)一個類型或者類型成員使用@Deprecated修飾的話,編譯器將不鼓勵使用這個被標(biāo)注的程序元素。而且這種修飾具有一定的 “延續(xù)性”:如果我們在代碼中通過繼承或者覆蓋的方式使用了這個過時的類型或者成員,雖然繼承或者覆蓋后的類型或者成員并不是被聲明為 @Deprecated,但編譯器仍然要報警。
          值得注意,@Deprecated這個annotation類型和javadoc中的 @deprecated這個tag是有區(qū)別的:前者是java編譯器識別的,而后者是被javadoc工具所識別用來生成文檔(包含程序成員為什么已經(jīng)過 時、它應(yīng)當(dāng)如何被禁止或者替代的描述)。
          在java5.0,java編譯器仍然象其從前版本那樣尋找@deprecated這個javadoc tag,并使用它們產(chǎn)生警告信息。但是這種狀況將在后續(xù)版本中改變,我們應(yīng)在現(xiàn)在就開始使用@Deprecated來修飾過時的方法而不是 @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)閉編譯器對類、方法、成員變量、變量初始化的警告。在java5.0,sun提供的javac編譯器為我們提供了-Xlint選項(xiàng)來使編 譯器對合法的程序代碼提出警告,此種警告從某種程度上代表了程序錯誤。例如當(dāng)我們使用一個generic collection類而又沒有提供它的類型時,編譯器將提示出"unchecked warning"的警告。

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

          annotation語法允許在annotation名后跟括號,括號中是使用逗號分割的name=value對用于為annotation的成員賦值:
          清單3:

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


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

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

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

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

          @SuppressWarnings("unchecked")


          3。Annotation語法:

          在上一個章節(jié)中,我們看到書寫marker annotation和單一成員annotation的語法。下面本人來介紹一下完整的語法:

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

          下面我們將定義一個 annotation類型名為Reviews,它有一個由@Review annotation數(shù)組構(gòu)成的成員。這個@Review annotation類型有三個成員:"reviewer"是一個字符串,"comment" 是一個具有默認(rèn)值的可選的字符串,"grade"是一個Review.Grade枚舉類型值。
          清單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語法的另一個重要規(guī)則是沒有程序成員可以有多于一個的同一annotation實(shí)例。例如在一個類中簡單的放置多個@Review annotation。這也是在上面代碼中定義@Reviews annotation類型數(shù)組的原因。

          4。Annotation成員類型和值:

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

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

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


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

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

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

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

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

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

          最后,請注意,枚舉類型定義中不允許任何的修飾符修飾其枚舉值。

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

          三、Annotation工作原理:

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

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

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

          回想一下,前面介紹的@Reviews annotation,如果這個annotation類型被聲明為runtime retention的話,我們通過下面的代碼來訪問@Reviews annotation的成員值:
          清單9:

          AnnotatedElement target = WhizzBangClass.class; //獲得被查詢的AnnotatedElement
          // 查詢AnnotatedElement的@Reviews annotation信息
          Reviews annotation = target.getAnnotation(Reviews.class);
          // 因?yàn)?#64;Reviews annotation類型的成員為@Review annotation類型的數(shù)組,
          // 所以下面聲明了Review[] reviews保存@Reviews annotation類型的value成員值。
          Review[] reviews = annotation.value();
          // 查詢每個@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類型是一個非凡的接口,所以兩者之間存在著某些差異:

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

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

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

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

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

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


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

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


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

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

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

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


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

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


          五、Meta-Annotation

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

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

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

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

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

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

          Documented是一個marker annotation,沒有成員。

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

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

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

          六、總結(jié):

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


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

          posted on 2007-09-06 19:11 草兒 閱讀(60243) 評論(15)  編輯  收藏 所屬分類: java

          Feedback

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

          # re: Java Annotation手冊 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ù)  更多評論
            

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

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

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

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

          # 展開一下@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í)例不會被繼承")
          public void mtd();
          }

          @UdtAnnotation
          abstract class MySuperClass {
          @UdtAnnotation("該方法必須被覆蓋,因此當(dāng)前注解實(shí)例不會被繼承")
          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ù)  更多評論
            

          # 展開一下@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無法通過反射獲得
          @Retention(RetentionPolicy.RUNTIME)
          // 默認(rèn)能修飾所有的ElementType中枚舉的所有內(nèi)容,這里讓他只能修飾類型和方法
          @Target({ElementType.TYPE, ElementType.METHOD})
          public @interface Reviews {
          int KKK = 12; // public static final 類似借口

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

          // 靜態(tà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的方法都是無參方法,有參方法由vm調(diào)用,在實(shí)例化annotation對象時使用
          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中會有警告提示,但javac不會有
          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中會有警告提示,但javac不會有
          interface Demo4 extends Demo1.Reviews { }


            回復(fù)  更多評論
            

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

          # re: Java Annotation手冊 2008-12-04 14:05 javafuns
          不錯,寫的不錯  回復(fù)  更多評論
            

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

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

          # re: Java Annotation手冊 2010-06-29 10:35 fuaiia
          無所謂 誰抄襲誰的 我們 本著拿來主義使用就對了   回復(fù)  更多評論
            

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

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

          主站蜘蛛池模板: 肃北| 虎林市| 彩票| 英山县| 枞阳县| 安溪县| 丹棱县| 洪泽县| 威信县| 宁海县| 博乐市| 东乡| 兴安盟| 淮北市| 开原市| 南溪县| 越西县| 邻水| 长沙县| 贵溪市| 兰西县| 红桥区| 杂多县| 甘孜县| 高淳县| 林周县| 广东省| 绥棱县| 新津县| 天长市| 巴里| 桂东县| 勃利县| 怀来县| 南溪县| SHOW| 东宁县| 休宁县| 修文县| 德格县| 涞源县|