JAVA從J2SE5開始提供名為annotation(注釋,標註)的功能。Java的annotation,可以附加在package, class, method, field等上面,相當於給它們添加了額外的輔助資訊。附加在package, class, method, field等上的Annotation,如果沒有外部解析工具等對其加以解析和處理的情況,本身不會對Java的原始碼或class等產生任何影響,也不 會對它們的執行產生任何影響。
但藉助外部工具,比如javac,EJB容器等,可以對附加在package, class, method, field的annotation進行解析,可以根據annotation而做出相應的處理,比如運行時改變對象/方法的行為。
Java標準Annotation
@Deprecated 相當於Javadoc的@deprecated,被@Deprecated標註的對象class, method等被註明為不推薦使用。主要用於javac等編譯工具。
@Override 註明對象method重載了父類的方法。javac等編譯工具編譯時間會根據此Annotation判斷重載方法是否正確。
@SuppressWarnings 告訴javac等編譯器忽略所指定的特定的警告資訊。
@Target 被定義的annotation可以附加在那些對象上。
@Retention annotation的作用期間。
Java標準Annotation的使用
@Deprecated:
@Deprecated
public class TestBean {
…
}
@SuppressWarnings:
@SuppressWarnings("serial ")
public class TestBean implements java.io.Serializable {
…
}
@SuppressWarnings(value = {"serial ", "unchecked "})
public String doSth() {
…
}
@Override:
@Override
public String doSth() {
…
}
Annotation的定義
定義方法:
@interface Annotation名 {定義體}
定義例1:
public @interface MyAnnotation {}
該例定義了一個無任何屬性/方法的Annotation。
定義例2:
public @interface MyAnnotation {
public String value();
}
該例定義了只有一個方法為value()的Annotation。一般來說,只有一個方法的Annotation,方法名一定定義為value。
定義例3:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyAnnotation {
public String value();
public String [] multiValues();
int number() default 0;
}
該例定義了一個具有多方法的Annotation。並設定其中一個方法number的預設值為0。multiValues 方法定義為數群組類型。Annotation定義可以用MetaAnnotation(元注釋)修飾。MetaAnnotation有以下2個:
@Retention
@Target
我們將在以下對@Retention與@Target加以說明。
@Retention
@Retention 可以設定為RetentionPolicy類型的值。
例:
@Retention(RetentionPolicy.RUNTIME)
RetentionPolicy的值 說明
RetentionPolicy.CLASS annotation資訊將被編譯器編譯時間儲存在class檔案中,但執行時不會在VM裝載。也就是說不能在執行時動態取得annotation資訊。未設定@Retention時這將是預設設定值。
RetentionPolicy.RUNTIME annotation資訊將被編譯器編譯時間儲存在class檔案中,執行時也會被VM裝載。
RetentionPolicy.SOURCE annotation資訊將被編譯器編譯時間捨棄掉。
@Target
@Target表明Annotation可以附加在哪種JAVA元素之上,可以設定為java.lang.annotation.ElementType數群組類型的值。
使用例1:
@Target(ElementType.METHOD)
使用例2:
@Target(value={ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.LOCAL_VARIABLE, ElementType.METHOD})
ElementType是一個枚舉類型,它具有以下定義:
ElementType值 說明
ElementType.ANNOTATION_TYPE 應用於其他註解的元註解
ElementType.CONSTRUCTOR 建構函式
ElementType.FIELD 欄位
ElementType.LOCAL_VARIABLE 方法中的本地變數
ElementType.METHOD 方法
ElementType.PACKAGE 包
ElementType.PARAMETER 方法的參數
ElementType.TYPE 類,介面或者枚舉聲明
自訂 Java Annotation
Annotation是一種特殊的interface。所以可以在annotation裡定義方法,屬性;也可以讓某個類從annotation繼承(implements)。
下面從簡單地範例開始,讓我們一步步加深對annotation的瞭解。
無任何方法/屬性Annotation範例:
MyAnnotation0.java
package com.test.annotation;
public @interface MyAnnotation0 {
}
MyAnnotation0為一個無任何方法和屬性的annotation。
使用MyAnnotation0:
TestMyAnnotation0.java
@MyAnnotation0
public class TestMyAnnotation0 {
@MyAnnotation0
public void testMethod() {
}
}
具有一個value方法Annotation範例:
MyAnnotation1.java
public @interface MyAnnotation1 {
/**
* value method
* @return value
*/
public String value();
}
MyAnnotation1具有一個名為value的方法。
MyAnnotation1使用:
TestMyAnnotation1.java
@MyAnnotation1("hello ")
public class TestMyAnnotation1 {
@MyAnnotation1(value="world ")
public void testMethod() {
}
}
可以通過@Annotation名(方法名1=值1, 方法名2=值2,
…)的形式給annotation賦值。只有一個方法的時候,可以直接省略為:@Annotation名(值1)
的賦值形式。當方法返回一個數組時,可以用 方法名={值1, 值2, …}給其賦值。
具有一個value方法和一個屬性Annotation範例:
如果必要,還可以在annotation裡為其定義屬性。如下:
MyAnnotation2.java
@interface MyAnnotation2 {
public String value();
public String myProperty = "hello world ";
}
其中,myProperty只能申明為public或無public修飾(無public修飾時也預設為public)為static, final屬性(即使不寫也預設為static, final)。
使用例:
TestMyAnnotation2
class TestMyAnnotation2 {
public static void main(String[] args) {
System.out.println(MyAnnotation2.myProperty);
}
@MyAnnotation2("")
public void testMethod1() {
}
}
上例會列印出:
hello world
複雜型annotation的定義與使用
本節介紹較為複雜的annotation定義與使用。
先看代碼:
MyAnnotation3.java
public @interface MyAnnotation3 {
public String value();
public String[] multiValues();
int number() default 0;
}
MyAnnotation3具有一個返回String的value方法,返回String[]的multiValues 方法;還有一個返回int 的number方法。其中number方法具有預設值0。
使用例:
TestMyAnnotation3.java
class TestMyAnnotation3 {
@MyAnnotation3(value = "call testMethod1 ", multiValues={"1 ", "2 "}, number = 1)
public void testMethod1() {
}
@MyAnnotation3(value = "call testMethod2 ", multiValues={"1 ", "2 "})
public void testMethod2() {
}
}
number具有預設值,所以標註時可以不為其賦值。其餘方法則必須通過上面介紹的方法賦值。multiValues返回一個String[]數組,所以可以通過multiValues={"1", "2"}為其賦值。
這樣說來,annotation到底能起什麼作用呢?
1, 編譯工具或其他工具可以根據被附加在代碼裡的annotation資訊自動組建組態檔案或文檔等外部檔案。
比如,sun公司就提供了apt(Annotation Processing Tool)
工具,apt工具是一個可以處理annotation的命令列工具,apt提供了在編譯期針對原始碼層級的解析,並可以在解析時產生新的原始碼和其他文
件,同時還可以對產生的原始碼進行編譯。
2, 其他程式可以在運行時動態解析將要被執行的程式裡的annotation資訊,並根據被附加的annotation資訊來執行不同的操作。
比如,EJB3規範就比較廣泛地使用了annotation特性。比如只要在POJO為class註明@Stateless注釋,EJB容器便會
根據此 annotation把該POJO註冊為無狀態的Session
Bean。EJB3使用了annotation大大地簡化了EJB的開發和配置過程。我們會在其他文章裡專門介紹EJB
Annotation的原理與使用方法,這裡不做詳述。
本文通過一個簡單地例子來說明怎麼在運行期動態解析annotation。Apt工具的使用我們會在近期其他文章裡對其加以介紹。
比如,我們定義了MyAnnotation3注釋:
MyAnnotation3.java
package com.test.annotation;
import java.lang.annotation.Annotation;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation3 {
public String value();
public String[] multiValues();
int number() default 0;
}
上面定義了一個名為MyAnnotation3的注釋。
我們再定義一個GetMyAnnotation類,該類使用了MyAnnotation3注釋:
GetMyAnnotation.java:
package com.test.annotation.test;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import javax.ejb.EJB;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import com.test.annotation.MyAnnotation3;
// 為GetMyAnnotation類附加MyAnnotation3 注釋
@MyAnnotation3(value = "Class GetMyAnnotation ", multiValues = {"1 ","2 "})
public class GetMyAnnotation {
// 為testField1屬性附加MyAnnotation3 注釋
@MyAnnotation3(value = "call testField1 ", multiValues={"1 "}, number = 1)
private String testField1;
// 為testMethod1方法附加MyAnnotation3 注釋
@MyAnnotation3(value = "call testMethod1 ", multiValues={"1 ", "2 "}, number = 1)
public void testMethod1() {
}
@Deprecated
@MyAnnotation3(value = "call testMethod2 ", multiValues={"3 ", "4 ", "5 "})
public void testMethod2() {
}
}
上面的例子GetMyAnnotation非常簡單,裡面沒有任何功能,但是分別為類(class),屬性(field),方法(method)申明(附加)了MyAnnotation3 注釋。
下面我們用程式TestMyAnnotation3對GetMyAnnotation裡MyAnnotation3注釋進行解析。
運行時解析annotation
TestMyAnnotation3.java
public class TestMyAnnotation3 {
public static void main(String[] args) {
System.out.println("--Class Annotations-- ");
if (GetMyAnnotation.class.isAnnotationPresent(MyAnnotation3.class )) {
System.out.println("[GetMyAnnotation].annotation: ");
MyAnnotation3 classAnnotation = GetMyAnnotation.class.getAnnotation(MyAnnotation3.class );
printMyAnnotation3(classAnnotation);
}
System.out.println("--Fields Annotations-- ");
Field [] fields = GetMyAnnotation.class.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(MyAnnotation3.class )) {
System.out.println("[GetMyAnnotation. " + field.getName() + "].annotation: ");
MyAnnotation3 fieldAnnotation = field.getAnnotation(MyAnnotation3.class );
printMyAnnotation3(fieldAnnotation);
}
}
System.out.println("--Methods Annotations-- ");
Method[] methods = GetMyAnnotation.class.getDeclaredMethods();
for (Method method : methods) {
System.out.println("[GetMyAnnotation. " + method.getName() + "].annotation: ");
if (method.isAnnotationPresent(MyAnnotation3.class )) {
MyAnnotation3 methodAnnotation = method.getAnnotation(MyAnnotation3.class );
printMyAnnotation3(methodAnnotation);
}
}
}
private static void printMyAnnotation3(MyAnnotation3 annotation3) {
if (annotation3 == null ) {
return;
}
System.out.println("{value= " + annotation3.value());
String multiValues = "";
for (String value: annotation3.multiValues()) {
multiValues += ", " + value;
}
System.out.println("multiValues= " + multiValues);
System.out.println("number= " + annotation3.number() + "} ");
}
}
輸出:
--Class Annotations--
[GetMyAnnotation].annotation:
{value=Class GetMyAnnotation
multiValues=,1,2
number=0}
--Fields Annotations--
[GetMyAnnotation.testField1].annotation:
{value=call testField1
multiValues=,1
number=1}
--Methods Annotations--
[GetMyAnnotation.testMethod1].annotation:
{value=call testMethod1
multiValues=,1,2
number=1}
[GetMyAnnotation.testMethod2].annotation:
{value=call testMethod2
multiValues=,3,4,5
number=0}
JDK1.5以後的版本提供的跟annotation有關的介面:
interface java.lang.reflect.AnnotatedElement {
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass);
<T extends Annotation> T getAnnotation(Class<T> annotationClass);
Annotation[] getAnnotations();
Annotation[] getDeclaredAnnotations();
}
該介面主要用來取得附加在類(class),構造方法(constructor),屬性(field),方法(method),包(package)上的annotation資訊。
JDK1.5與此有關的幾個類都實現了AnnotatedElement介面:
java.lang.reflect.AccessibleObject,
java.lang.reflect.Class,
java.lang.reflect.Constructor,
java.lang.reflect.Field,
java.lang.reflect.Method,
java.lang.reflect.Package
所以可以利用反射(reflection)功能在程式裡動態解析附加的annotation資訊。
總結:
本文通過舉例簡單地說明了怎麼動態解析annotation,大家可以舉一反三,利用Java的annotation特性,完成更複雜功能等。
Trackback:http://yelinsen.iteye.com/blog/1051886