afunms

          My Software,My Dream—Forge a more perfect NMS product.

          充分利用Java元數據

          http://www.oracle.com/technology/global/cn/pub/articles/hunter_meta.html

          了解如何使用 J2SE 5.0 中提供的元數據批注

          最新的 J2SE 5.0 版本(也以代號 "Tiger" 著稱)為 Java 語言引進了許多變化,這些變化旨在使 Java 編程更有表現力、更加開發人員友好和更安全。我在 2003 年 9 月的一篇題目為“Java 即將發生巨大變化”的文章中介紹了許多 Java 新特性。我沒有介紹的一個重大的變化 — 那時還沒有完整概述它 — 是 Java 的元數據工具。從本文開始,在一個新的分為四個部分的文章系列中,我將從一年前離開的地方繼續向您顯示如何充分利用 Java 的元數據。

          在第一篇文章中,我將說明元數據的用途并演示如何使用在核心的 J2SE 資料庫中提供的元數據批注。

          在第二篇文章中,我將顯示如何編寫您自己的批注(首先編寫類似 @Copyright 的簡單的批注,然后看看與核心語言中內置的那些批注類似的更高級的批注)。

          在第三篇文章中,我將演示工具如何在構建時使用批注(創建新的源文件或支持文件)以及程序如何在運行時使用批注(以改變代碼的行為)。

          在最后的第四篇文章中,我將介紹如何利用在 JSR-181 下開發的標準元數據批注的幫助使編輯和部署 Web 服務在未來變得更容易(Oracle 是 JSR-181 的專家組的成員,并且是在開發工具中增加對設計時元數據的支持的一個積極的擁護者。)

          元數據

          我承認當我第一次看到 JSR-175 的提案“用于 Java 編程語言的元數據工具”(在 2004 年 9 月發布;Oracle 也是該專家組的成員)時,我預測它將創建必須放在 JAR 的 META-INF 目錄下的另一個屬性文件,或者必須與 JAR 捆綁的另一個 XML 部署描述符。幸運的是,這不是元數據要做的。事實上,它正好相反。Java 的新的元數據工具提供了從 Java 代碼內部批注 Java 代碼的一種標準方式。它使您能夠在要說明的元素的旁邊放置描述性的元數據。

          當討論元數據時,您將經常看到幾個類似的術語,因此下面提供了一個小術語表來幫助您了解它們的差異:

          術語 定義
          元數據 關于數據的數據。JSR-175 的目標是在 Java 語言中提供元數據工具。
          批注 一種特殊的 Java 結構,用來修飾類、方法、字段、參數、變量、構造器或包。它是 JSR-175 選擇用來提供元數據的工具。
          批注類型 具有特殊實施的各種命名批注
          屬性 由批注指定的一個特殊的元數據項目。有時可以和批注交替使用

           

          例如:富士蘋果有一個屬性:它是紅色的。假定有一個 FujiApple 類,您可以使用 @Color 批注類型的一個批注來指定它的顏色。通過這么做,您就提供了關于蘋果的元數據。

          自 1.0 版以來在 Java 中一直存在對元數據的需求。Java 從來沒有提供記錄元數據的標準機制,因而我們編程人員找到了各種技巧和竅門使用任意的工具來添加元數據。您看到在 J2SE 1.4 和更低版本中使用元數據的一些地方有:

          • transient 關鍵字
          • Serializable 標記接口
          • SingleThreadModel servlet 接口
          • web.xml 部署描述符內部的元素
          • META-INF/MANIFEST.MF 文件
          • BeanInfo 接口
          • @deprecated Javadoc 注釋
          • 所有的 XDoclet Javadoc 標記。

           

          當使用這些技巧時,您可能沒有想到您正在添加元數據,但事實上您的確在添加元數據。上述方法存在的問題是它們都是解決同一問題的不同方法,但通用性不好。每一種方法都至少有一個缺點在新的元數據工具中得到了解決。

          對于這個列表中的一些方法,局限很明顯。使用關鍵字不能伸縮;您不能使用用戶自己定義的關鍵字。標記接口沒有提供除它們的存在性之外的任何信息(即,它們沒有帶參數),并且它們只能處理類,而不能處理字段或方法或包。

          列表中的其他一些方法可能看起來合理。使用 XML 支持文件似乎是個好主意,而事實上在許多情況下仍是個好主意。但對于我們使用 XML 文件的許多用途,例如指示類的哪一個方法應當看作是 web 服務,在 Java 代碼內部將規則直接放在方法的旁邊將更加高效。利用元數據,您可以使 XML 描述符文件僅包含與部署相關的決策。

          該列表中可能最高效的元數據的用法是 @deprecated Javadoc 注釋和在其鏡像中創建的 XDoclet 標記。這可能是 JSR-175 語法為什么看起來與 @deprecated 標記非常類似的原因(正如我們將在下一部分中看到的那樣)。

          批注

          批注可以很容易地附加到代碼結構上。您可以寫一個 "at" 符號 (@),然后是批注類型名稱,并將批注直接放在要批注的項目前面。下面是一個簡單的例子:

          import javax.jws.WebService;
          import javax.jws.WebMethod;
          @WebService
          public class HelloWorldService {
          @WebMethod
          public String helloWorld() {
          return "Hello World!";
          }
          }
          

           

          當部署在正確的環境中時,增加 @WebService@WebMethod 批注將指示 web 服務環境將該類變為 web 服務。

          您可以批注方法、類、字段、參數、變量、構造器甚至整個包(利用一個特殊的外部 package-info.java 文件)。批注可以在括號內帶任意數量的命名參數。下面是使用批注進行修飾以創建 web 服務的一個更高級的示例類。它包含了一個理論上的 JNDI 環境變量查找:

          @WebService(
          name = "PingService",
          targetNamespace="http://acme.com/ping"
          )
          @SOAPBinding(
          style=SOAPBinding.Style.RPC,
          use=SOAPBinding.Use.LITERAL
          )
          public class Ping {
          public @env double level = 500.0;  // JNDI lookup
          public @WebMethod(operationName = "Foo") {
          void foo() { }
          }
          }
          

           

          這個例子顯示了附加到類、變量和方法(在類上實際上有兩個方法)上的批注。@env 批注沒有任何參數,因此它不需要括號。其他批注有一個或更多的命名參數。

          當創建新的批注類型時,您將限定允許哪些參數名以及它們的類型。批注接受的類型是嚴格限定的;它們只可以是基本類型、String、Class、枚舉類型、批注類型和前面這些類型的數組。傳遞的參數必須始終是非空的編譯時常量。

          要了解本示例中顯示的批注有什么效果必須等到本系列的第四篇文章。讓我們開始看看 J2SE 5.0 提供的簡單的批注類型:@Override、@Deprecated@SuppressWarnings。

          內置的批注

          當我們看這三種標準的用戶級批注時,必須考慮:在可以提供的所有可能的批注類型中,為什么 Tiger 恰恰提供三種?原因是提供大量的標準批注并不是目標所在。

          JSR-175 的宗旨嚴格規定了它是要定義一個元數據工具。編寫自定義批注類型的任務留給了編程人員,而編寫一組標準的批注類型的任務留給了其他 JSR。例如,有一個新的名稱為“Java 平臺的通用批注”的 JSR-250,其宗旨是“為 J2SE 和 J2EE 平臺中的通用的語義概念開發適用于各種技術的批注”。JSR-250 計劃在 2005 年春天的某個時候在 javax.annotations 程序包中提供它的標準的批注集。還有之前提到的 JSR-181,它將使得在 J2EE 容器中編寫 Web 服務變得更容易(我們將在本系列中的第四篇文章中進行介紹)。事實上,大多數新的企業 JSR(從 Servlets 2.5 到 EJB 3.0 到 JDBC 4.0)都在考慮批注可以提供哪些優點。

          @Override

          第一個 J2SE 標準批注 @Override 使您能夠在代碼中增加新的可選的編譯器檢查。它在方法中存在表示該方法用于覆蓋父類中的方法。如果編譯器檢測到該方法實際上沒有覆蓋任何東西,那么將出現編譯錯誤。經常使用,@Override 可以幫助您避免當方法標記沒有完全匹配時 — 當多態變為(您可以稱之為)“單態” ("unimorphism") 時 — 將得到的細微的 bug。

          例如,以下代碼可能看起來很合理:

          public class OverrideExample {
          @Override
          public boolean equals(OverrideExample obj) {
          return false;
          }
          }
          

           

          然而,當您編譯 OverrideExample.java 時,您將得到一個錯誤,該錯誤指示一個細微的問題。

          % javac OverrideExample.java
          javac OverrideExample.java
          OverrideExample.java:3: method does not override a method from its superclass
          @Override
          ^
          1 error
          

           

          通過提示編譯器您希望進行覆蓋,使編譯器能夠捕獲到 equals() 方法帶 Object 類型參數的細微 bug。

          @Override 批注在實際中有用嗎?只有當您是一個愿意用 @Override 來標記每一個覆蓋方法的非常嚴謹的編程人員時才有用。我們中有多少人能聲稱可以達到這種嚴謹程度?我認為我不能。可能 IDE 將找到一種方式來鼓勵或強制使用 @Override

          @Deprecated

          第二種標準批注是 @Deprecated,它與 @deprecated Javadoc 標記有幾乎相同的行為。您可以用類似以下的方式來使用它:

          public class DeprecatedExample {
          @Deprecated
          public static void badMethod() {
          }
          }
          public class DeprecatedUser {
          public static void main(String[] args) {
          DeprecatedExample.badMethod();
          }
          }
          

           

          The @Deprecated 批注看起來非常像 @deprecated 標記,除了它出現在注釋外面的方法或類聲明的前面,并且有一個大寫字母 "D"。如果您試圖編譯上面的代碼,javac 將產生警告:

          % javac Deprecated*.java
          Note:DeprecatedUser.java uses or overrides a deprecated API.
          Note:Recompile with -Xlint:deprecation for details.
          1 error
          

           

          如果您遵循警告的建議并用 -Xlint:deprecation 進行編譯,那么您將得到關于警告的詳細信息:

          % javac -Xlint:deprecation
          DeprecatedUser.java:3: warning: [deprecation] badMethod() in DeprecatedExample
          has been deprecated
          DeprecatedExample.badMethod();
          

           

          @Deprecated 批注比 @Override 更有用嗎?我不這么認為。該批注不支持任何參數,因此與 Javadoc 標記不同,您不能提供一個字符串來說明不贊成使用該方法并推薦一個替代的方法進行使用。@Deprecated 批注提供的價值實際上比 @deprecated 標記少。該批注唯一的優勢是您可以通過編程的方式在運行時檢測不贊成使用的項目。因此,傳統觀點認為應當同時使用 @deprecated 標記和 @Deprecated 標記,一個用于文檔,另一個用于運行時反射。

          我覺得很不幸 JSR-175 沒有選擇對 @Deprecated 做更多的工作。至少該批注應當復制 @deprecated 標記的功能,包含一個字符串說明,從而編譯器可以將其與“不贊成使用” (Deprecation) 警告一起輸出。利用額外的參數,@Deprecated 還可以接收 "isError" 布爾類型參數,以指示是否完全不鼓勵使用該方法或者使用它將被認為是編譯錯誤(利用解釋錯誤原因的清楚的自定義說明來進行完善)。查看 C# 的示例 1 找到屬性 [Obsolete],該屬性正好實現了這一點,它被證明非常有用。

          @SuppressWarnings

          J2SE 提供的最后一個批注是 @SuppressWarnings。該批注的作用是給編譯器一條指令,告訴它對被批注的代碼元素內部的某些警告保持靜默。

          一點背景:J2SE 5.0 為 Java 語言增加了幾個新的特性,并且和它們一起增加了許多新的警告并承諾在將來增加更多的警告。您可以為 "javac" 增加 -Xlint 參數來控制是否報告這些警告(如上面的 @Deprecated 部分所示)。

          默認情況下,Sun 編譯器以簡單的兩行的形式輸出警告。通過添加 -Xlint:keyword 標記(例如 -Xlint:finally),您可以獲得關鍵字類型錯誤的完整說明。通過在關鍵字前面添加一個破折號,寫為 -Xlint:-keyword,您可以取消警告。(-Xlint 支持的關鍵字的完整列表可以在 javac 文檔頁面上找到。)下面是一個清單:

          關鍵字 用途
          deprecation 使用了不贊成使用的類或方法時的警告
          unchecked 執行了未檢查的轉換時的警告,例如當使用集合時沒有用泛型 (Generics) 來指定集合保存的類型。
          fallthrough 當 Switch 程序塊直接通往下一種情況而沒有 Break 時的警告。
          path 在類路徑、源文件路徑等中有不存在的路徑時的警告。
          serial 當在可序列化的類上缺少 serialVersionUID 定義時的警告。
          finally 任何 finally 子句不能正常完成時的警告。
          all 關于以上所有情況的警告。

           

          @SuppressWarnings 批注允許您選擇性地取消特定代碼段(即,類或方法)中的警告。其中的想法是當您看到警告時,您將調查它,如果您確定它不是問題,您就可以添加一個 @SuppressWarnings 批注,以使您不會再看到警告。雖然它聽起來似乎會屏蔽潛在的錯誤,但實際上它將提高代碼安全性,因為它將防止您對警告無動于衷 — 您看到的每一個警告都將值得注意。

          下面是使用 @SuppressWarnings 來取消 deprecation 警告的一個例子:

          public class DeprecatedExample2 {
          @Deprecated
          public static void foo() {
          }
          }
          public class DeprecatedUser2 {
          @SuppressWarnings(value={"deprecation"})
          public static void main(String[] args) {
          DeprecatedExample2.foo();
          }
          }
          

           

          @SuppressWarnings 批注接收一個 "value" 變量,該變量是一個字符串數組,它指示將取消的警告。合法字符串的集合隨編譯器而變化,但在 JDK 上,可以傳遞給 -Xlint 的是相同的關鍵字集合(非常方便)。并且要求編譯器忽略任何它們不能識別的關鍵字,這在您使用一些不同的編譯器時非常方便。

          因為 @SuppressWarnings 批注僅接收一個參數,并為該參數使用了特殊的名稱 "value",所以您可以選擇省略 value=,作為一種方便的縮寫:

          public class DeprecatedUser2 {
            @SuppressWarnings({"deprecation"})
          public static void main(String[] args) {
              DeprecatedExample2.foo();
            }
          }

          您可以將單個數組參數中的任意數量的字符串值傳遞給批注,并在任何級別上放置批注。例如,以下示例代碼指示將取消整個類的 deprecation 警告,而僅在 main() 方法代碼內取消 unchecked 和 fallthrough 警告:

          import java.util.*;

          @SuppressWarnings({"deprecation"})
          public class NonGenerics {

            @SuppressWarnings({"unchecked","fallthrough"})
          public static void main(String[] args) {
              Runtime.runFinalizersOnExit();

              List list = new ArrayList();
              list.add("foo");
            }

            public static void foo() {
              List list = new ArrayList();
              list.add("foo");
            }
          }

          @SuppressWarnings 是否比前兩個批注更有用?絕對是這樣。不過,在 JDK 1.5.0 版本中還沒有完全支持該批注,如果您用 1.5.0 來嘗試它,那么它將類似無操作指令。調用 -Xlint:-deprecation 也沒有任何效果。Sun 沒有聲明什么時候將增加支持,但它暗示這將在即將推出的一個 dot 版本中實現。

          posted on 2008-10-01 14:12 afunms 閱讀(269) 評論(0)  編輯  收藏


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           

          My Links

          News

          留言簿(18)

          隨筆檔案

          相冊

          搜索

          最新評論

          閱讀排行榜

          主站蜘蛛池模板: 镶黄旗| 临清市| 徐汇区| 曲水县| 永顺县| 潞西市| 彩票| 奉贤区| 百色市| 揭阳市| 盐边县| 萨嘎县| 宁明县| 谷城县| 揭东县| 鄂伦春自治旗| 兖州市| 三门峡市| 和林格尔县| 象山县| 城固县| 敦煌市| 盈江县| 五台县| 宜都市| 静宁县| 海伦市| 新龙县| 久治县| 辽阳市| 顺昌县| 夹江县| 聂拉木县| 东城区| 栾城县| 磐石市| 太仆寺旗| 东至县| 高尔夫| 阿克苏市| 游戏|