一、注解定義
JVM5.0定義了4個標准的元注解:
- @Target,
- @Retention,
- @Documented
- @Inherited
1. @Target
作用:用於描述注解的使用范圍
取值ElementType有:
- CONSTRUCTOR:用於描述構造器
- FIELD:用於描述域
- LOCAL_VARIABLE:用於描述局部變量
- METHOD:用於描述方法
- PACKAGE:用於描述包
- PARAMETER:用於描述參數
- TYPE:用於描述類、接口(包括注解類型) 或enum聲明
舉例:
@Target(ElementType.TYPE)
public @interface Table {
/**
* 數據表名稱注解,默認值為類名稱
* @return
*/
public String tableName() default "className";
}
@Target(ElementType.FIELD)
public @interface NoDBColumn {
}
注解Table可以用於注解類、接口(包括注解類型)或enum聲明,而注解NoDBColumn僅用於注解類的成員變量。
2. @Retention
作用:用於描述注解的生命周期
取值RetentionPolicy有:
- SOURCE:在源文件中有效(即源文件保留)
- CLASS:在class文件中有效(即class保留)
- RUNTIME:在運行時有效(即運行時保留)
舉例:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
public String name() default "fieldName";
public String setFuncName() default "setField";
public String getFuncName() default "getField";
public boolean defaultDBValue() default false;
}
Column注解的的RetentionPolicy的屬性值是RUNTIME,這樣注解處理器可以通過反射,獲取到該注解的屬性值,從而去做一些運行時的邏輯處理
3. Documented
作用:用於描述其它類型的annotation應該被作為被標注的程序成員的公共API,因此可以被例如javadoc此類的工具文檔化。Documented是一個標記注解,沒有成員。
舉例:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Column {
public String name() default "fieldName";
public String setFuncName() default "setField";
public String getFuncName() default "getField";
public boolean defaultDBValue() default false;
}
4. @Inherited
@Inherited 元注解是一個標記注解,@Inherited闡述了某個被標注的類型是被繼承的。如果一個使用了Inherited修飾的annotation類型被用於一個class,則這個annotation將被用於該class的子類。
注意:Inherited annotation類型是被標注過的class的子類所繼承。類並不從它所實現的接口繼承annotation,方法並不從它所重載的方法繼承annotation。
當Inherited annotation類型標注的annotation的Retention是RetentionPolicy.RUNTIME,則反射API增強了這種繼承性。如果我們使用java.lang.reflect去查詢一個Inherited annotation類型的annotation時,反射代碼檢查將展開工作:檢查class和其父類,直到發現指定的annotation類型被發現,或者到達類繼承結構的頂層。
實例代碼:
@Inherited
public @interface Greeting {
public enum FontColor{ BULE,RED,GREEN};
String name();
FontColor fontColor() default FontColor.GREEN;
}
5. 自定義注解
- 使用interface自定義注解,自動繼承java.lang.annotation.Annotation接口。
- 不能繼承其他的注解或接口。
- 每一個方法實際上是聲明了一個配置參數。方法的名稱就是參數的名稱,返回值類型就是參數的類型(只能是基本類型、Class、String、enum)。可以通過default聲明參數的默認值。
定義注解格式:
public @interface 注解名(定義體)
注解參數的可支持數據類型:
- 所有基本數據類型(int,float,boolean,byte,double,char,long,short)
- String類型
- Class類型
- enum類型
- Annotation類型
- 以上所有類型的數組
自定義注解的定義和具體實現可以見下一章節。
二、注解實現
創建注解處理器,利用反射對注解加以處理。
AnnotatedElement 接口是所有程序元素(Class、Method和Constructor)的父接口,關於AnnotatedElement點詳細信息可以參考
JavaDoc:Interface AnnotatedElement。
所以程序通過反射獲取了某個類的AnnotatedElement對象之后,程序就可以調用該對象的如下四個個方法來訪問Annotation信息:
- 方法1:
<T extends Annotation> T getAnnotation(Class<T> annotationClass):
返回改程序元素上存在的、指定類型的注解,如果該類型注解不存在,則返回null。
-
方法2:Annotation[] getAnnotations():返回該程序元素上存在的所有注解。
-
方法3:boolean is AnnotationPresent(Class<?extends Annotation> annotationClass):判斷該程序元素上是否包含指定類型的注解,存在則返回true,否則返回false.
-
方法4:Annotation[] getDeclaredAnnotations():返回直接存在於此元素上的所有注釋。與此接口中的其他方法不同,該方法將忽略繼承的注釋。(如果沒有注釋直接存在於此元素上,則返回長度為零的一個數組。)該方法的調用者可以隨意修改返回的數組;這不會對其他調用者返回的數組產生任何影響。
舉例:
注解聲明
1.FruitName
package annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitName {
String value() default "";
}
2.FruitColor
package annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitColor {
public enum Color{BlUE, RED, GREEN};
Color fruitColor() default Color.GREEN;
}
3.FruitProvider
package annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitProvider {
public int id() default -1;
public String name() default "";
public String address() default "";
}
注解使用
package annotation;
import annotation.FruitColor.Color;
public class Apple {
@FruitName("Apple")
private String appleName;
@FruitColor(fruitColor=Color.RED)
private String appleColor;
@FruitProvider(id=1, name="紅富士集團", address="紅富士大廈")
private String appleProvider;
public String getAppleName() {
return appleName;
}
public void setAppleName(String appleName) {
this.appleName = appleName;
}
public String getAppleColor() {
return appleColor;
}
public void setAppleColor(String appleColor) {
this.appleColor = appleColor;
}
public String getAppleProvider() {
return appleProvider;
}
public void setAppleProvider(String appleProvider) {
this.appleProvider = appleProvider;
}
public void display(){
System.out.println("The fruit name is:" + appleName + ", its color is: " + appleColor + ".");
}
}
注解實現
package annotation;
import java.lang.reflect.Field;
public class FruitInfoUtil {
public static void getFruitInfo(Class<?> clazz){
String strFruitName= "水果名稱: ";
String strFruitColor= "水果顏色: ";
String strFruitProvider= "供應商信息: ";
Field[] fields = clazz.getDeclaredFields();
for(Field field : fields){
if (field.isAnnotationPresent(FruitName.class)){
FruitName fruitName = (FruitName)field.getAnnotation(FruitName.class);
strFruitName += fruitName.value();
System.out.println(strFruitName);
}
if (field.isAnnotationPresent(FruitColor.class)){
FruitColor fruitColor = (FruitColor)field.getAnnotation(FruitColor.class);
strFruitColor += fruitColor.fruitColor().toString();
System.out.println(strFruitColor);
}
if (field.isAnnotationPresent(FruitProvider.class)){
FruitProvider fruitProvider = (FruitProvider)field.getAnnotation(FruitProvider.class);
strFruitProvider += " 供應商編號:" + fruitProvider.id() + " 供應商名稱:" + fruitProvider.name() + " 供應商地址:" + fruitProvider.address();
System.out.println(strFruitProvider);
}
}
}
}
執行結果:
水果名稱: Apple
水果顏色: RED
供應商信息: 供應商編號:1 供應商名稱:紅富士集團 供應商地址:紅富士大廈