dyerac |
|
|||
dyerac In Java |
公告
日歷
統計
導航常用鏈接留言簿(5)隨筆分類(49)
隨筆檔案(36)
文章分類(11)文章檔案(10)相冊dyerac搜索積分與排名
最新隨筆最新評論
閱讀排行榜評論排行榜 |
基礎學習教程:Java Annotation入門摘要:本文針對java初學者或者annotation初次使用者全面地說明了annotation的使 用方法、定義方式、分類。初學者可以通過以上的說明制作簡單的annotation程序,但是對于一些高級的annotation應用(例如使用自定義 annotation生成javabean映射xml文件)還需要進一步的研究和探討。涉及到深入annotation的內容,作者將在后文《Java Annotation高級應用》中談到。 同時,annotation運行存在兩種方式:運行時、編譯時。上文中討論的都是在運行時的annotation應用,但在編譯時的annotation應用還沒有涉及, 一、為什么使用Annotation: 在JAVA應用中,我們常遇到一些需要使用模版代碼。例如,為了編寫一個JAX-RPC web service,我們必須提供一對接口和實現作為模版代碼。如果使用annotation對遠程訪問的方法代碼進行修飾的話,這個模版就能夠使用工具自動生成。 另外,一些API需要使用與程序代碼同時維護的附屬文件。例如,JavaBeans需要一個 BeanInfo Class與一個Bean同時使用/維護,而EJB則同樣需要一個部署描述符。此時在程序中使用annotation來維護這些附屬文件的信息將十分便利 而且減少了錯誤。 二、Annotation工作方式: 在5.0版之前的Java平臺已經具有了一些ad hoc annotation機制。比如,使用transient修飾符來標識一個成員變量在序列化子系統中應被忽略。而@deprecated這個 javadoc tag也是一個ad hoc annotation用來說明一個方法已過時。從Java5.0版發布以來,5.0平臺提供了一個正式的annotation功能:允許開發者定義、使用 自己的annoatation類型。此功能由一個定義annotation類型的語法和一個描述annotation聲明的語法,讀取annotaion 的API,一個使用annotation修飾的class文件,一個annotation處理工具(apt)組成。 annotation并不直接影響代碼語義,但是它能夠工作的方式被看作類似程序的工具或者類庫,它會反過來對正在運行的程序語義有所影響。annotation可以從源文件、class文件或者以在運行時反射的多種方式被讀取。 當然annotation在某種程度上使javadoc tag更加完整。一般情況下,如果這個標記對java文檔產生影響或者用于生成java文檔的話,它應該作為一個javadoc tag;否則將作為一個annotation. 三、Annotation使用方法: 1.類型聲明方式: 通常,應用程序并不是必須定義annotation類型,但是定義annotation類型并非難事。Annotation類型聲明于一般的接口聲明極為類似,區別只在于它在interface關鍵字前面使用"@"符號。 annotation類型的每個方法聲明定義了一個annotation類型成員,但方法聲明不必 有參數或者異常聲明;方法返回值的類型被限制在以下的范圍:primitives、String、Class、enums、annotation和前面類 型的數組;方法可以有默認值。 下面是一個簡單的annotation類型聲明: 清單1: /** 代碼中只定義了一個annotation類型RequestForEnhancement. 2.修飾方法的annotation聲明方式: annotation 是一種修飾符,能夠如其它修飾符(如public、static、final)一般使用。習慣用法是annotaions用在其它的修飾符前面。 annotations由"@+annotation類型+帶有括號的成員-值列表"組成。這些成員的值必須是編譯時常量(即在運行時不變)。 A:下面是一個使用了RequestForEnhancement annotation的方法聲明: 清單2: @RequestForEnhancement( B:當聲明一個沒有成員的annotation類型聲明時,可使用以下方式: /**
清單4:
@Preliminary public class TimeTravel { ... }
清單5:
/**
清單6:
@Copyright("2002
結合上面所講的,我們在這里建立一個簡單的基于annotation測試框架。首先我們需要一個annotation類型來表示某個方法是一個應該被測試工具運行的測試方法。
清單7:
import java.lang.annotation.*;
在上面的代碼中,@Retention(RetentionPolicy.RUNTIME)這個meta-annotation表示了此類型的 annotation將被虛擬機保留使其能夠在運行時通過反射被讀取。而@Target(ElementType.METHOD)表示此類型的 annotation只能用于修飾方法聲明。
下面是一個簡單的程序,其中部分方法被上面的annotation所標注:
清單8:
public class Foo {
下面文字表示了如何運行這個基于annotation的測試工具:
清單9:
$ java RunTests Foo
根據annotation的使用方法和用途主要分為以下幾類:
1.內建Annotation――Java5.0版在java語法中經常用到的內建Annotation:
@Deprecated用于修飾已經過時的方法;
@Override用于修飾此方法覆蓋了父類的方法(而非重載);
@SuppressWarnings用于通知java編譯器禁止特定的編譯警告。
下面代碼展示了內建Annotation類型的用法:
清單10:
package com.bjinfotech.practice.annotation;
下面是一個使用annotation進行方法測試的sample:
AnnotationDefineForTestFunction類型定義如下:
清單11: package com.bjinfotech.practice.annotation;
清單12: 版權聲明:本文可以自由轉載,轉載時請務必以超鏈接形式標明文章原始出處和作者信息及本聲明 作者:cleverpig(作者的Blog:http://blog.matrix.org.cn/page/cleverpig) 原文:http://www.matrix.org.cn/resource/article/44/44062_Java+Annotation+Apt.html 關鍵字:java,annotation,apt 前言: 前不久在matrix上先后發表了《java annotation 入門》、《java annotation 手冊》兩 篇文章,比較全面的對java annotation的語法、原理、使用三方面進行了闡述。由于《入門》中的簡單例程雖然簡單明了的說明了annotation用法,但給大家的感覺可能 是意猶未見,所以在此行文《java annotation高級應用》,具體實例化解釋annotation和annotation processing tool(APT)的使用。望能對各位的有所幫助。 一、摘要: 《java annotation高級應用》具體實例化解釋annotation和annotation processing tool(APT)的使用。望能對各位的有所幫助。本文列舉了用于演示annotation的BRFW演示框架、演示APT的apt代碼實例,并對其進行 較為深度的分析,希望大家多多提意見。 二、annotation實例分析 1.BRFW(Beaninfo Runtime FrameWork)定義: 本人編寫的一個annotation功能演示框架。顧名思義,BRFW就是在運行時取得bean信息的框架。 2.BRFW的功能: A.源代碼級annotation:在bean的源代碼中使用annotation定義bean的信息; B.運行時獲取bean數據:在運行時分析bean class中的annotation,并將當前bean class中field信息取出,功能類似xdoclet; C.運行時bean數據的xml綁定:將獲得的bean數據構造為xml文件格式展現。熟悉j2ee的朋友知道,這個功能類似jaxb。 3.BRFW框架: BRFW主要包含以下幾個類: A.Persistent類:定義了用于修飾類的固有類型成員變量的annotation。 B.Exportable類:定義了用于修飾Class的類型的annotation。 C.ExportToXml類:核心類,用于完成BRFW的主要功能:將具有Exportable Annotation的bean對象轉換為xml格式文本。 D.AddressForTest類:被A和B修飾過的用于測試目的的地址bean類。其中包含了地址定義所必需的信息:國家、省級、城市、街道、門牌等。 E.AddressListForTest類: 被A和B修飾過的友人通訊錄bean類。其中包含了通訊錄所必備的信息:友人姓名、年齡、電話、住址(成員為AddressForTest類型的 ArrayList)、備注。需要說明的是電話這個bean成員變量是由字符串類型組成的ArrayList類型。由于朋友的住址可能不唯一,故這里的住 址為由AddressForTest類型組成的ArrayList。 從上面的列表中,可以發現A、B用于修飾bean類和其類成員;C主要用于取出bean類的數據并將其作xml綁定,代碼中使用了E作為測試類;E中可能包含著多個D。 在了解了這個簡單框架后,我們來看一下BRFW的代碼吧! 4.BRFW源代碼分析: A.Persistent類: 清單1:
B.Exportable類: 清單2:
C.AddressForTest類: 清單3:
D.AddressListForTest類: 清單4:
E.ExportToXml類: 清單5:
在ExportToXml類之前的類比較簡單,這里必須說明一下ExportToXml類:此類的核心函數是exportObject和 exportFields方法,前者輸出對象的xml信息,后者輸出對象成員變量的信息。由于對象類型和成員類型的多樣性,所以采取了以下的邏輯: 在exportObject方法中,當對象類型為Collection和Map類型時,則需要遞歸調用exportObject進行處理; 而如果對象類型不是Collection和Map類型的話,將判斷對象類是否被Exportable annotation修飾過: 如果沒有被修飾,則直接輸出<對象類名>對象.toString()</對象類名>作為xml綁定結果的一部分; 如果被修飾過,則需要調用exportFields方法對對象的成員變量進行xml綁定。 在exportFields 方法中,首先取出對象的所有成員,然后獲得被Persisitent annotation修飾的成員。在其后的一句:field.setAccessible(true)是很重要的,因為bean類定義中的成員訪問修飾都 是private,所以為了避免java虛擬機檢查對私有成員的訪問權限,加上這一句是必需的。接著后面的語句便是輸出<成員名>成員值 </成員名>這樣的xml結構。像在exportObject方法中一般,仍然需要判斷成員類型是否為Collection和Map類型,如 果為上述兩種類型之一,則要在exportFields中再次調用exportObject來處理這個成員。 在main方法中,本人編寫了一段演示代碼:建立了一個由單個友人地址類(AddressForTest)組成的ArrayList作為通訊錄類(AddressForTest)的成員的通訊錄對象,并且輸出這個對象的xml綁定,運行結果如下: 清單6:
三、APT實例分析: 1.何謂APT? 根 據sun官方的解釋,APT(annotation processing tool)是一個命令行工具,它對源代碼文件進行檢測找出其中的annotation后,使用annotation processors來處理annotation。而annotation processors使用了一套反射API并具備對JSR175規范的支持。 annotation processors處理annotation的基本過程如下:首先,APT運行annotation processors根據提供的源文件中的annotation生成源代碼文件和其它的文件(文件具體內容由annotation processors的編寫者決定),接著APT將生成的源代碼文件和提供的源文件進行編譯生成類文件。 簡單的和前面所講的annotation 實例BRFW相比,APT就像一個在編譯時處理annotation的javac。而且從sun開發者的blog中看到,java1.6 beta版中已將APT的功能寫入到了javac中,這樣只要執行帶有特定參數的javac就能達到APT的功能。 2.為何使用APT? 使 用APT主要目的是簡化開發者的工作量,因為APT可以在編譯程序源代碼的同時,生成一些附屬文件(比如源文件、類文件、程序發布描述文字等),這些附屬 文件的內容也都是與源代碼相關的。換句話說,使用APT就是代替了傳統的對代碼信息和附屬文件的維護工作。使用過hibernate或者beehive等 軟件的朋友可能深有體會。APT可以在編譯生成代碼類的同時將相關的文件寫好,比如在使用beehive時,在代碼中使用annotation聲明了許多 struct要用到的配置信息,而在編譯后,這些信息會被APT以struct配置文件的方式存放。 3.如何定義processor? A.APT工作過程: 從 整個過程來講,首先APT檢測在源代碼文件中哪些annotation存在。然后APT將查找我們編寫的annotation processor factories類,并且要求factories類提供處理源文件中所涉及的annotation的annotation processor。接下來,一個合適的annotation processors將被執行,如果在processors生成源代碼文件時,該文件中含有annotation,則APT將重復上面的過程直到沒有新文 件生成。 B.編寫annotation processors: 編寫一個annotation processors需要使用java1.5 lib目錄中的tools.jar提供的以下4個包: com.sun.mirror.apt: 和APT交互的接口; com.sun.mirror.declaration: 用于模式化類成員、類方法、類聲明的接口; com.sun.mirror.type: 用于模式化源代碼中類型的接口; com.sun.mirror.util: 提供了用于處理類型和聲明的一些工具。 每 個processor實現了在com.sun.mirror.apt包中的AnnotationProcessor接口,這個接口有一個名為 “process”的方法,該方法是在APT調用processor時將被用到的。一個processor可以處理一種或者多種annotation類 型。 一個processor實例被其相應的工廠返回,此工廠為AnnotationProcessorFactory接口的實現。APT將調用工 廠類的getProcessorFor方法來獲得processor。在調用過程中,APT將提供給工廠類一個 AnnotationProcessorEnvironment 類型的processor環境類對象,在這個環境對象中,processor將找到其執行所需要的每件東西,包括對所操作的程序結構的參考,與APT通訊 并合作一同完成新文件的建立和警告/錯誤信息的傳輸。 提供工廠類有兩個方式:通過APT的“-factory”命令行參數提供,或者讓工廠類在APT的發現過程中被自動定位(關于發現過程詳細介紹請看http://java.sun.com/j2se/1.5.0/docs/guide/apt/GettingStarted.html)。前者對于一個已知的factory來講是一種主動而又簡單的方式;而后者則是需要在jar文件的META-INF/services目錄中提供一個特定的發現路徑: 在 包含factory類的jar文件中作以下的操作:在META-INF/services目錄中建立一個名為 com.sun.mirror.apt.AnnotationProcessorFactory 的UTF-8編碼文件,在文件中寫入所有要使用到的factory類全名,每個類為一個單獨行。 4.一個簡單的APT實例分析: A.實例構成: Review類:定義Review Annotation; ReviewProcessorFactory類:生成ReviewProcessor的工廠類; ReviewProcessor類:定義處理Review annotation的Processor; ReviewDeclarationVisitor類:定義Review annotation聲明訪問者,ReviewProcessor將要使用之對Class進行訪問。 runapt.bat:定義了使用自定義的ReviewProcessor對Review類源代碼文件進行處理的APT命令行。 B.Review類: 清單7:
C.ReviewProcessorFactory類: 清單8:
D.ReviewProcessor類: 清單9:
E.ReviewDeclarationVisitor類: 清單10:
F.runapt.bat文件內容如下: 清單11:
四、參考資源: http://java.sun.com/j2se/1.5.0/docs/guide/apt/GettingStarted.html 作者的Blog:http://blog.matrix.org.cn/page/cleverpig 五、源代碼下載: [下載文件] ------------------------------------------- 1、引入 l 編程的一個最新趨勢,尤其是 Java 編程,就是使用元數據 l 元數據可以用于創建文檔,跟蹤代碼中的依賴性,甚至執行基本編譯時檢查 l 許多元數據工具(如 Xdoclet)將這些功能添加到核心 Java 語言中,暫時成為 Java 編程功能的一部分 l Javadoc是元數據工具,但除了生成文檔之外,沒有固定、實用、標準化的方式將數據用于其他用途,而且HTML代碼經常混入到Javadoc輸出中,更進一步降低了其用于任何其它目的的價值 l JSR 175,Java編程語言的元數據工具,為將元數據合并到核心 Java 語言中提供了正式理由和說明 l Tiger 增加了Annotation的新功能,將一個更通用的元數據工具合并到核心 Java 語言中 l Annotation是可以添加到代碼中的修飾符,可以用于包聲明、類型聲明、構造函數、方法、域變量、參數和變量 l Tiger包含內置的Annotation,還支持自己編寫的定制Annotation l 本部分將概述元數據的優點,并介紹Tiger的內置Annotation 2、元數據的價值 一般來說,元數據的好處分為三類:文檔編制、編譯器檢查和代碼分析 (1)文檔編制 l 代碼級文檔最常被引用,但對于將元數據添加到 Java 語言中來說,文檔編制可能是最不相關的理由 l 因為Javadoc已經提供了非常容易理解和健壯的方法來文檔化代碼 (2)編譯時檢查 l 元數據更重要的優點是編譯器可以使用它來執行基本的編譯時檢查 l 具體情況請參看后面介紹的Tiger內置Annotation:@Override (3)代碼分析 l 元數據工具的最好功能就是可以使用額外數據來分析代碼 l 簡單的案例就是:許多時候,方法的參數類型或返回類型實際上不是該方法想要的類型;例如,參數類型可能是Object,但方法可能僅使用Integer,這在覆蓋超類的方法時很容易發生;元數據可以指示代碼分析工具:雖然參數類型是 Object,但 Integer 才是真正需要的 l 復雜的案例就是:即使是簡單EJB系統中也具有很強的依賴性和復雜性,要具有 Home和Remote接口,以及本地的Home 和Remote接口,以及一個實現類,保持所有這些類同步非常困難;好的工具(如XDoclet)可以管理所有這些依賴性,并確保這些沒有“代碼級”聯系,但有“邏輯級”聯系的類保持同步;元數據在這里確實可以發揮它的作用 3、Annotation基礎 l Annotation的格式是:@Annotation名 l 在Annotation需要數據時,通過name=value的形式提供 l 代碼中可以用很多Annotation,有些Annotation會具有相同的Annotation類型 l Annotation類型和Annotation的概念類似于類和對象的概念 l Annotation有三種基本種類: Ø 標記Annotation:只有Annotation名,不包含數據,如@MarkerAnnotation Ø 單值Annotation:只有單一的數據,可以簡化name=value的形式為value形式,如@SingleValueAnnotation("my data") Ø 完整格式的Annotation:有多個數據成員,如@FullAnnotation(var1="data value 1", var2="data value 2", var3="data value 3") l 可以使用花括號向Annotation變量提供值數組,如 @TODOItems({ // Curly braces indicate an array of values is being supplied
@TODO(
severity=TODO.CRITICAL,
item="Add functionality to calculate the mean of the student's grades",
assignedTo="Brett McLaughlin"
),
@TODO(
severity=TODO.IMPOTANT,
item="Print usage message to screen if no command-line flags specified",
assignedTo="Brett McLaughlin"
),
@TODO(
severity=TODO.LOW,
item="Roll a new website page with this class's new features",
assignedTo="Jason Hunter"
)
})
4、Tiger內置Annotation (1)@Override l @Override只用于方法,指明改方法覆蓋超類中的對應方法 l 簡單例子: public class OverrideTester {
public OverrideTester() {
}
@Override public String toString() { return super.toString() + " [Override Tester Implementation]";
}
@Override public int hashCode() { return toString().hashCode();
}
}
l @Override可以檢查輸入錯誤導致無法覆蓋超類方法的問題,例如hashCode()錯誤的輸入為hasCode(),在編譯時就會報錯: The method hasCode() of type OverrideTester must override a superclass method
l 這個便捷的小功能將幫助快速捕獲打字錯誤 (2)@Deprecated l 同樣只用于方法,指明該方法不應該再使用了 l 簡單例子: public class DeprecatedClass {
@Deprecated public void doSomething() { System.out.println("Deprecated method!");
// some code
}
public void doSomethingElse() {
// This method presumably does what doSomething() does, but better
}
}
l 單獨編譯正常通過,如果通過覆蓋或調用Deprecated的方法,編譯器會給出警告信息 l 注:本人在Eclipse 3.1M4環境中測試,根本不起作用(即使是改了編譯參數,why?),在命令行下使用-Xlint:deprecated參數,JAVAC只給出警告信息,編譯還是通過的 (3)@SuppressWarnings l Tiger的泛型功能使得編譯器對類型的安全性進行檢查,特別是Java集合,如下面的例子: public void nonGenericsMethod() {
List wordList = new ArrayList(); // no typing information on the List
wordList.add("foo"); // causes error on list addition }
l 編譯器會給出下面的警告信息: Type safety: The method add(Object) belongs to the raw type List. References to generic
type List<E> should be parameterized
l 這對于Tiger的代碼是很有幫助的,但對于JDK1.4及以前版本,不斷的收到無關的警告信息是很煩人的 l 可以使用@SuppressWarnings來阻止指定類型的警告信息,如: @SuppressWarnings(value = { "unchecked" })
public void nonGenericsMethod() {
List wordList = new ArrayList(); // no typing information on the List
wordList.add("foo"); // causes error on list addition
}
l 傳遞給@SuppressWarnings的類型值是一個數組,因此可以同時阻止多種類型的警告信息 l 類型值是由編譯器廠商所指定的,所以上面的例子我在Eclipse 3.1M4環境和命令行中測試,都不起作用,大概是類型值沒有指定對吧 --------------------------------------- 1、自定義Annotation類型 (1)定義Annotation類型 l 使用@interface聲明Annotation類型 public @interface InProgress {
}
l 使用Annotation類型 public class TestAnnotation {
@InProcess
public void test() {
}
}
l 如果Annotation類型和使用它的類不在相同的包中,可以import Annotation類型,以便直接使用 @InProgress (2)添加成員 l Annotation類型可以有成員變量,以提供有用的信息 l 定義數據成員不需要定義getter和setter方法,只需要定義一個以成員名稱命名的方法,并指定返回類型為需要的數據類型 l 簡單的例子: public @interface TODO {
String value();
}
l 使用帶成員的Annotation類型: public class TestAnnotation {
@InProcess
@TODO("Need to finish this method later")
public void test() {
}
}
(3)設置缺省值 l 要為Annotation類型的成員設置缺省值,需要在聲明成員時使用default關鍵字: public @interface GroupTODO {
public enum Severity {
CRITICAL, IMPORTANT, TRIVIAL, DOCUMENTATION
};
Severity severity() default Severity.IMPORTANT; String item();
String assignedTo();
String dateAssigned();
}
l 當然,缺省值的類型必須與成員變量聲明的類型完全相同 l 下面是使用缺省值的例子: public class TestAnnotation {
@InProcess
@GroupTODO(
item="Need to finish this method later",
assignedTo="nelson_tu",
dateAssigned="2005/02/05"
)
public void test() {
}
}
l 下面是改寫缺省值的例子: public class TestAnnotation {
@InProcess
//@TODO("Need to finish this method later")
@GroupTODO(
severity=GroupTODO.Severity.DOCUMENTATION,
item="Need to finish this method later",
assignedTo="nelson_tu",
dateAssigned="2005/02/05"
)
public void test() {
}
}
2、元Annotation l 元Annotation就是Annotation的Annotation,JDK5提供了4種預定義的元Annotation (1)@Target l @Target指定Annotation類型可以應用的程序元素,以便在其它程序元素中誤用Annotation類型 l 程序元素的類型由java.lang.annotation.ElementType枚舉類定義: package java.lang.annotation;
public enum ElementType {
TYPE, // Class, interface, or enum (but not annotation)
FIELD, // Field (including enumerated values)
METHOD, // Method (does not include constructors)
PARAMETER, // Method parameter
CONSTRUCTOR, // Constructor
LOCAL_VARIABLE, // Local variable or catch clause
ANNOTATION_TYPE, // Annotation Types (meta-annotations)
PACKAGE // Java package
}
l 下面是使用@Target的例子: @Target({ElementType.TYPE,
ElementType.METHOD,
ElementType.CONSTRUCTOR,
ElementType.ANNOTATION_TYPE})
public @interface TODO {
String value();
}
(2)@Retention l @Retention和 Java 編譯器處理Annotation類型的方式有關 l 這些方式由 package java.lang.annotation;
public enum RetentionPolicy {
SOURCE, // Annotation is discarded by the compiler
CLASS, // Annotation is stored in the class file, but ignored by the VM
RUNTIME // Annotation is stored in the class file and read by the VM
}
l 使用@Retention的例子參看后面的@Documented (3)@Documented l @Documented指明需要在Javadoc中包含Annotation(缺省是不包含的) l 下面是一個使用@Documented的例子: @Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface InProcess {
} l 使用@Documented的一個技巧就是指定保持性策略為RetentionPolicy.RUNTIME:這樣,Annotation就會保留在編譯后的類文件中并且由虛擬機加載,然后Javadoc就可以抽取出Annotation,添加到類的HTML文檔中 (4)@Inherited l @Inherited最復雜、使用最少、也最容易造成混淆的一個 l 假設使用@InProgress 標記一個正在開發的類,只要正確應用@Documented,Annotation信息就會出現在Javadoc中;現在要編寫一個新類,擴展那個正在開發的類,那么使用子類,或者查看它的文檔,根本沒法表明還有什么地方沒有完成;而本來是希望@InProgress 的Annotation信息會被帶到子類中,這就需要使用@Inherited了 l 下面是這樣的例子: @Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface InProcess {
}
評論:
|
![]() |
|
Copyright © dyerac in java... | Powered by: 博客園 模板提供:滬江博客 |