備注: 本文大部分內容及代碼均來自網上,具體引用看末尾 8.參考引用**
- 概念及作用
- JDK注解
- 元注解
- 自定義注解
- 注解處理器
- Java 8 中注解新特性
- 注解的優缺點及與XML的比較
- 參考引用
1. 概念及作用
-
1.1 概念
- 注解即元數據,就是源代碼的元數據
- 注解在代碼中添加信息提供了一種形式化的方法,可以在后續中更方便的 使用這些數據
- Annotation是一種應用于類、方法、參數、變量、構造器及包聲明中的特殊修飾符。它是一種由JSR-175標準選擇用來描述元數據的一種工具。
-
1.2 作用
- a. 生成文檔
- b. 跟蹤代碼依賴性,實現替代配置文件功能,減少配置。如Spring中的一些注解
- c. 在編譯時進行格式檢查,如@Override等
-
1.3 意義
- 注解之前,XML被廣泛的應用于描述元數據,XML的維護越來越糟糕
- 在需要緊耦合的地方,比XML該容易維護,閱讀更方便
- 在需要比較多參數設置時,使用xml更方便,而在將某個方法聲明為服務時這種緊耦合的情況下,比較適合注解
- XML是松耦合的,注解是緊耦合的
- 對于“XML VS 注解” ,可以google了解一下
- 對于XML和注解的使用,要具體問題具體分析
- Java的annotation沒有行為,只能有數據,實際上就是一組鍵值對而已。通過解析(parse)Class文件就能把一個annotation需要的鍵值對都找出來
2. JDK注解
- 2.1 Override: 保證編譯時 要重寫方法的正確性
- 2.2 Deprected: 提示該方法已經過時
-
2.3 SuppressWarnings: 關閉特定警告信息,參數如下:
- all: to suppress all warnings
- boxing: to suppress warnings relative to boxing/unboxing operations
- cast: to suppress warnings relative to cast operations
- dep-ann: to suppress warnings relative to deprecated annotation
- deprecation: to suppress warnings relative to deprecation
- fallthrough: to suppress warnings relative to missing breaks in switch statements
- finally: to suppress warnings relative to finally block that don’t return
- hiding: to suppress warnings relative to locals that hide variable
- incomplete-switch: to suppress warnings relative to missing entries in a switch statement (enum case)
- nls: to suppress warnings relative to non-nls string literals
- null: to suppress warnings relative to null analysis
- rawtypes: to suppress warnings relative to un-specific types when using generics on class params
- restriction: to suppress warnings relative to usage of discouraged or forbidden references
- serial: to suppress warnings relative to missing serialVersionUID field for a serializable class
- static-access: to suppress warnings relative to incorrect static access
- synthetic-access: to suppress warnings relative to unoptimized access from inner classes
- unchecked: to suppress warnings relative to unchecked operations
- unqualified-field-access: to suppress warnings relative to field access unqualified
- unused: to suppress warnings relative to unused code
3. 元注解
-
2.1 定義
- 負責注解其他注解
-
2.2 四種元注解
- @Documented
- @Target
- @Retention
- @Inherited
-
2.3 @Documented
- 用于描述其他類型的annotation應該被作為被標注的程序成員的公共API
- 可以用于javadoc 此類的工具文檔化
- Document是一個標記注解,沒有成員
-
2.4 @Target
- 說明Annotation所修飾的對象范圍,即描述注解的使用范圍
- Annotation可被用于packages,types(類,接口,枚舉,Annotation類型),類型成員(方法,構造方法,成員變量,枚舉值),方法參數和本地變量(如循環變量,catch參數)
- 在Annotation類型的聲明中使用了target可更加明晰其修飾目標
- 取值有:
類型 | 用途 |
---|---|
CONSTRUCTOR | 用于描述構造器 |
FIELD | 用于描述域 |
LOCAL_VARIABLE | 用于描述局部變量 |
METHOD | 用于描述方法 |
PACKAGE | 用于描述包 |
PARAMETER | 用于描述參數 |
TYPE | 用于描述類、接口(包括注解類型) 或enum聲明 |
-
2.5 @Retention
- 定義了Annotation被保留的時間長短
- 表示需要在什么級別保存該注釋信息,用于描述注解的生命周期(即被描述的注解在什么范圍內有效)
- Retention meta-annotation類型有唯一的value作為成員,它的取值來自java.lang.annotation.RetentionPolicy的枚舉類型值。具體實例:
類型 | 用途 | 說明 |
---|---|---|
SOURCE | 在源文件中有效(即源文件保留) | 僅出現在源代碼中,而被編譯器丟棄 |
CLASS | 在class文件中有效(即class保留) | 被編譯在class文件中 |
RUNTIME | 在運行時有效(即運行時保留) | 編譯在class文件中 |
-
2.6 @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類型被發現,或者到達類繼承結構的頂層。
4. 自定義注解
- 格式
public @interface 注解名{
定義體
}
- 注解參數的可支持數據類型:
- 所有基本數據類型(int,float,double,boolean,byte,char,long,short)
- String 類型
- Class類型
- enum類型
- Annotation類型
- 以上所有類型的數組
- 修飾符只能是public 或默認(default)
- 參數成員只能用基本類型byte,short,int,long,float,double,boolean八種基本類型和String,Enum,Class,annotations及這些類型的數組
- 如果只有一個參數成員,最好將名稱設為"value"
- 注解元素必須有確定的值,可以在注解中定義默認值,也可以使用注解時指定,非基本類型的值不可為null,常使用空字符串或0作默認值
- 在表現一個元素存在或缺失的狀態時,定義一下特殊值來表示,如空字符串或負值
- 示例:
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;
/**
* test注解
* @author zlw
*
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TestAnnotation {
/**
* id
* @return
*/
public int id() default -1;
/**
* name
* @return
*/
public String name() default "";
}
5. 注解處理器
- 使用注解的過程,重要的是創建注解處理器
- Java SE5 擴展了反射機制的API,以幫助程序員快速的構造自定義注解處理器
- 注解處理器類庫: java.lang.reflect.AnnotatedElement
- Java使用Annotation接口來代表程序元素面前的注解,該接口是所有Annotation類型的接口。
- java.lang.annotation.Annotation 是所有Annotation類型的父接口
- java.lang.reflect.AnnotatedElement 可以接受注解的程序元素,該接口主要有Class(類定義),Constructor(構造器定義),Filed(類的成員變量定義),Method(類的方法定義),Package(類的包定義),
- java.lang.reflect包所有提供的反射API擴充了讀取運行時Annotation信息的能力,當一個Annotation類型被定義為運行時Annotation后,該注解才能運行時可見,當class文件被裝載時被保存在class文件中的Annotation才會被虛擬機讀取
- AnnotatedElement 接口是所有程序元素(Class、Method和Constructor)的父接口,所以程序通過反射獲取了某個類的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():返回直接存在于此元素上的所有注釋。與此接口中的其他方法不同,該方法將忽略繼承的注釋。(如果沒有注釋直接存在于此元素上,則返回長度為零的一個數組。)該方法的調用者可以隨意修改返回的數組;這不會對其他調用者返回的數組產生任何影響
- 示例:
/***********注解聲明***************/
/**
* 水果名稱注解
* @author peida
*
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitName {
String value() default "";
}
/**
* 水果顏色注解
* @author peida
*
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitColor {
/**
* 顏色枚舉
* @author peida
*
*/
public enum Color{ BULE,RED,GREEN};
/**
* 顏色屬性
* @return
*/
Color fruitColor() default Color.GREEN;
}
/**
* 水果供應者注解
* @author peida
*
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitProvider {
/**
* 供應商編號
* @return
*/
public int id() default -1;
/**
* 供應商名稱
* @return
*/
public String name() default "";
/**
* 供應商地址
* @return
*/
public String address() default "";
}
/***********注解使用***************/
public class Apple {
@FruitName("Apple")
private String appleName;
@FruitColor(fruitColor=Color.RED)
private String appleColor;
@FruitProvider(id=1,name="陜西紅富士集團",address="陜西省西安市延安路89號紅富士大廈")
private String appleProvider;
public void setAppleColor(String appleColor) {
this.appleColor = appleColor;
}
public String getAppleColor() {
return appleColor;
}
public void setAppleName(String appleName) {
this.appleName = appleName;
}
public String getAppleName() {
return appleName;
}
public void setAppleProvider(String appleProvider) {
this.appleProvider = appleProvider;
}
public String getAppleProvider() {
return appleProvider;
}
public void displayName(){
System.out.println("水果的名字是:蘋果");
}
}
/***********注解處理器***************/
public class FruitInfoUtil {
public static void getFruitInfo(Class<?> clazz){
String strFruitName=" 水果名稱:";
String strFruitColor=" 水果顏色:";
String strFruitProvicer="供應商信息:";
Field[] fields = clazz.getDeclaredFields();
for(Field field :fields){
if(field.isAnnotationPresent(FruitName.class)){
FruitName fruitName = (FruitName) field.getAnnotation(FruitName.class);
strFruitName=strFruitName+fruitName.value();
System.out.println(strFruitName);
}
else if(field.isAnnotationPresent(FruitColor.class)){
FruitColor fruitColor= (FruitColor) field.getAnnotation(FruitColor.class);
strFruitColor=strFruitColor+fruitColor.fruitColor().toString();
System.out.println(strFruitColor);
}
else if(field.isAnnotationPresent(FruitProvider.class)){
FruitProvider fruitProvider= (FruitProvider) field.getAnnotation(FruitProvider.class);
strFruitProvicer=" 供應商編號:"+fruitProvider.id()+" 供應商名稱:"+fruitProvider.name()+" 供應商地址:"+fruitProvider.address();
System.out.println(strFruitProvicer);
}
}
}
}
/***********輸出結果***************/
public class FruitRun {
/**
* @param args
*/
public static void main(String[] args) {
FruitInfoUtil.getFruitInfo(Apple.class);
}
}
====================================
水果名稱:Apple
水果顏色:RED
供應商編號:1 供應商名稱:陜西紅富士集團 供應商地址:陜西省西安市延安路89號紅富士大廈
6. Java 8 中注解新特性
- @Repeatable 元注解,表示被修飾的注解可以用在同一個聲明式或者類型加上多個相同的注解(包含不同的屬性值)
- @Native 元注解,本地方法
- java8 中Annotation 可以被用在任何使用 Type 的地方
//初始化對象時
String myString = new @NotNull String();
//對象類型轉化時
myString = (@NonNull String) str;
//使用 implements 表達式時
class MyList<T> implements @ReadOnly List<@ReadOnly T>{
...
}
//使用 throws 表達式時
public void validateValues() throws @Critical ValidationFailedException{
...
}
7. 注解的優缺點及與XML的比較
- 優:
- 方便,簡潔,配置信息和 Java 代碼放在一起,有助于增強程序的內聚性
- 若要對配置項進行修改,不得不修改 Java 文件,重新編譯打包應用
- 缺:
- 分散到各個class文件中,維護性較差
- 配置項編碼在 Java 文件中,可擴展性差
- 與XML比較:
- 簡潔
- 沒有XML配置更強大
- 不便于修改,不便于統一管理