備注: 本文大部分內(nèi)容及代碼均來(lái)自網(wǎng)上,具體引用看末尾 8.參考引用**
- 概念及作用
- JDK注解
- 元注解
- 自定義注解
- 注解處理器
- Java 8 中注解新特性
- 注解的優(yōu)缺點(diǎn)及與XML的比較
- 參考引用
1. 概念及作用
-
1.1 概念
- 注解即元數(shù)據(jù),就是源代碼的元數(shù)據(jù)
- 注解在代碼中添加信息提供了一種形式化的方法,可以在后續(xù)中更方便的 使用這些數(shù)據(jù)
- Annotation是一種應(yīng)用于類糊饱、方法、參數(shù)仪缸、變量谣膳、構(gòu)造器及包聲明中的特殊修飾符竟终。它是一種由JSR-175標(biāo)準(zhǔn)選擇用來(lái)描述元數(shù)據(jù)的一種工具榴捡。
-
1.2 作用
- a. 生成文檔
- b. 跟蹤代碼依賴性诱桂,實(shí)現(xiàn)替代配置文件功能,減少配置帘瞭。如Spring中的一些注解
- c. 在編譯時(shí)進(jìn)行格式檢查淑掌,如@Override等
-
1.3 意義
- 注解之前,XML被廣泛的應(yīng)用于描述元數(shù)據(jù),XML的維護(hù)越來(lái)越糟糕
- 在需要緊耦合的地方,比XML該容易維護(hù),閱讀更方便
- 在需要比較多參數(shù)設(shè)置時(shí),使用xml更方便,而在將某個(gè)方法聲明為服務(wù)時(shí)這種緊耦合的情況下,比較適合注解
- XML是松耦合的蝶念,注解是緊耦合的
- 對(duì)于“XML VS 注解” 抛腕,可以google了解一下
- 對(duì)于XML和注解的使用,要具體問(wèn)題具體分析
- Java的annotation沒(méi)有行為芋绸,只能有數(shù)據(jù),實(shí)際上就是一組鍵值對(duì)而已担敌。通過(guò)解析(parse)Class文件就能把一個(gè)annotation需要的鍵值對(duì)都找出來(lái)
2. JDK注解
- 2.1 Override: 保證編譯時(shí) 要重寫方法的正確性
- 2.2 Deprected: 提示該方法已經(jīng)過(guò)時(shí)
-
2.3 SuppressWarnings: 關(guān)閉特定警告信息,參數(shù)如下:
- 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 定義
- 負(fù)責(zé)注解其他注解
-
2.2 四種元注解
- @Documented
- @Target
- @Retention
- @Inherited
-
2.3 @Documented
- 用于描述其他類型的annotation應(yīng)該被作為被標(biāo)注的程序成員的公共API
- 可以用于javadoc 此類的工具文檔化
- Document是一個(gè)標(biāo)記注解,沒(méi)有成員
-
2.4 @Target
- 說(shuō)明Annotation所修飾的對(duì)象范圍,即描述注解的使用范圍
- Annotation可被用于packages,types(類,接口,枚舉,Annotation類型),類型成員(方法,構(gòu)造方法,成員變量,枚舉值),方法參數(shù)和本地變量(如循環(huán)變量,catch參數(shù))
- 在Annotation類型的聲明中使用了target可更加明晰其修飾目標(biāo)
- 取值有:
類型 | 用途 |
---|---|
CONSTRUCTOR | 用于描述構(gòu)造器 |
FIELD | 用于描述域 |
LOCAL_VARIABLE | 用于描述局部變量 |
METHOD | 用于描述方法 |
PACKAGE | 用于描述包 |
PARAMETER | 用于描述參數(shù) |
TYPE | 用于描述類摔敛、接口(包括注解類型) 或enum聲明 |
-
2.5 @Retention
- 定義了Annotation被保留的時(shí)間長(zhǎng)短
- 表示需要在什么級(jí)別保存該注釋信息,用于描述注解的生命周期(即被描述的注解在什么范圍內(nèi)有效)
- Retention meta-annotation類型有唯一的value作為成員,它的取值來(lái)自java.lang.annotation.RetentionPolicy的枚舉類型值全封。具體實(shí)例:
類型 | 用途 | 說(shuō)明 |
---|---|---|
SOURCE | 在源文件中有效(即源文件保留) | 僅出現(xiàn)在源代碼中马昙,而被編譯器丟棄 |
CLASS | 在class文件中有效(即class保留) | 被編譯在class文件中 |
RUNTIME | 在運(yùn)行時(shí)有效(即運(yùn)行時(shí)保留) | 編譯在class文件中 |
-
2.6 @Inherited
- 是一個(gè)標(biāo)記注解
- 闡述了某個(gè)被標(biāo)注的類型是被繼承的
- 使用了@Inherited修飾的annotation類型被用于一個(gè)class,則這個(gè)annotation將被用于該class的子類
- @Inherited annotation類型是被標(biāo)注過(guò)的class的子類所繼承。類并不從實(shí)現(xiàn)的接口繼承annotation,方法不從它所重載的方法繼承annotation
- 當(dāng)@Inherited annotation類型標(biāo)注的annotation的Retention是RetentionPolicy.RUNTIME刹悴,則反射API增強(qiáng)了這種繼承性行楞。如果我們使用java.lang.reflect去查詢一個(gè)@Inherited annotation類型的annotation時(shí),反射代碼檢查將展開工作:檢查class和其父類颂跨,直到發(fā)現(xiàn)指定的annotation類型被發(fā)現(xiàn)敢伸,或者到達(dá)類繼承結(jié)構(gòu)的頂層。
4. 自定義注解
- 格式
public @interface 注解名{
定義體
}
- 注解參數(shù)的可支持?jǐn)?shù)據(jù)類型:
- 所有基本數(shù)據(jù)類型(int,float,double,boolean,byte,char,long,short)
- String 類型
- Class類型
- enum類型
- Annotation類型
- 以上所有類型的數(shù)組
- 修飾符只能是public 或默認(rèn)(default)
- 參數(shù)成員只能用基本類型byte,short,int,long,float,double,boolean八種基本類型和String,Enum,Class,annotations及這些類型的數(shù)組
- 如果只有一個(gè)參數(shù)成員,最好將名稱設(shè)為"value"
- 注解元素必須有確定的值,可以在注解中定義默認(rèn)值,也可以使用注解時(shí)指定,非基本類型的值不可為null,常使用空字符串或0作默認(rèn)值
- 在表現(xiàn)一個(gè)元素存在或缺失的狀態(tài)時(shí),定義一下特殊值來(lái)表示,如空字符串或負(fù)值
- 示例:
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. 注解處理器
- 使用注解的過(guò)程,重要的是創(chuàng)建注解處理器
- Java SE5 擴(kuò)展了反射機(jī)制的API,以幫助程序員快速的構(gòu)造自定義注解處理器
- 注解處理器類庫(kù): java.lang.reflect.AnnotatedElement
- Java使用Annotation接口來(lái)代表程序元素面前的注解,該接口是所有Annotation類型的接口恒削。
- java.lang.annotation.Annotation 是所有Annotation類型的父接口
- java.lang.reflect.AnnotatedElement 可以接受注解的程序元素,該接口主要有Class(類定義),Constructor(構(gòu)造器定義),Filed(類的成員變量定義),Method(類的方法定義),Package(類的包定義),
- java.lang.reflect包所有提供的反射API擴(kuò)充了讀取運(yùn)行時(shí)Annotation信息的能力,當(dāng)一個(gè)Annotation類型被定義為運(yùn)行時(shí)Annotation后,該注解才能運(yùn)行時(shí)可見,當(dāng)class文件被裝載時(shí)被保存在class文件中的Annotation才會(huì)被虛擬機(jī)讀取
- AnnotatedElement 接口是所有程序元素(Class池颈、Method和Constructor)的父接口,所以程序通過(guò)反射獲取了某個(gè)類的AnnotatedElement對(duì)象之后钓丰,程序就可以調(diào)用該對(duì)象的如下四個(gè)個(gè)方法來(lái)訪問(wèn)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():返回直接存在于此元素上的所有注釋。與此接口中的其他方法不同肥橙,該方法將忽略繼承的注釋魄宏。(如果沒(méi)有注釋直接存在于此元素上,則返回長(zhǎng)度為零的一個(gè)數(shù)組存筏。)該方法的調(diào)用者可以隨意修改返回的數(shù)組宠互;這不會(huì)對(duì)其他調(diào)用者返回的數(shù)組產(chǎn)生任何影響
- 示例:
/***********注解聲明***************/
/**
* 水果名稱注解
* @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;
}
/**
* 水果供應(yīng)者注解
* @author peida
*
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitProvider {
/**
* 供應(yīng)商編號(hào)
* @return
*/
public int id() default -1;
/**
* 供應(yīng)商名稱
* @return
*/
public String name() default "";
/**
* 供應(yīng)商地址
* @return
*/
public String address() default "";
}
/***********注解使用***************/
public class Apple {
@FruitName("Apple")
private String appleName;
@FruitColor(fruitColor=Color.RED)
private String appleColor;
@FruitProvider(id=1,name="陜西紅富士集團(tuán)",address="陜西省西安市延安路89號(hào)紅富士大廈")
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="供應(yīng)商信息:";
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=" 供應(yīng)商編號(hào):"+fruitProvider.id()+" 供應(yīng)商名稱:"+fruitProvider.name()+" 供應(yīng)商地址:"+fruitProvider.address();
System.out.println(strFruitProvicer);
}
}
}
}
/***********輸出結(jié)果***************/
public class FruitRun {
/**
* @param args
*/
public static void main(String[] args) {
FruitInfoUtil.getFruitInfo(Apple.class);
}
}
====================================
水果名稱:Apple
水果顏色:RED
供應(yīng)商編號(hào):1 供應(yīng)商名稱:陜西紅富士集團(tuán) 供應(yīng)商地址:陜西省西安市延安路89號(hào)紅富士大廈
6. Java 8 中注解新特性
- @Repeatable 元注解,表示被修飾的注解可以用在同一個(gè)聲明式或者類型加上多個(gè)相同的注解(包含不同的屬性值)
- @Native 元注解,本地方法
- java8 中Annotation 可以被用在任何使用 Type 的地方
//初始化對(duì)象時(shí)
String myString = new @NotNull String();
//對(duì)象類型轉(zhuǎn)化時(shí)
myString = (@NonNull String) str;
//使用 implements 表達(dá)式時(shí)
class MyList<T> implements @ReadOnly List<@ReadOnly T>{
...
}
//使用 throws 表達(dá)式時(shí)
public void validateValues() throws @Critical ValidationFailedException{
...
}
7. 注解的優(yōu)缺點(diǎn)及與XML的比較
- 優(yōu):
- 方便,簡(jiǎn)潔椭坚,配置信息和 Java 代碼放在一起予跌,有助于增強(qiáng)程序的內(nèi)聚性
- 若要對(duì)配置項(xiàng)進(jìn)行修改,不得不修改 Java 文件善茎,重新編譯打包應(yīng)用
- 缺:
- 分散到各個(gè)class文件中券册,維護(hù)性較差
- 配置項(xiàng)編碼在 Java 文件中,可擴(kuò)展性差
- 與XML比較:
- 簡(jiǎn)潔
- 沒(méi)有XML配置更強(qiáng)大
- 不便于修改,不便于統(tǒng)一管理