Annotation(注解)是Java 1.5中添加的特性,很多java框架都依賴注解來實現代碼級配置,相比XML配置更簡潔。要理解框架的運行原理,注解是一個基礎的概念。
注解的基本概念
網上、書本對注解的基本概念已經進行了很詳細的講解,這里不再重述,例如:
《Java核心技術 卷2 10.3,10.4,10.5》對注解進行了全面的講解,但翻譯的文字晦澀
http://www.importnew.com/10294.html 講解了注解的基本概念,特別是介紹了注解的歷史
http://www.importnew.com/11908.html 對java8中對注解的改進進行了介紹
從高層次來講,注解是與代碼緊耦合的元數據;從語法上來看,注解是一種特殊的接口。
注解的生存期與注解的處理
注解的兩個元注解@Target
和@Retention
定義了注解的使用目標和生存期。注解的生存期在java.lang.annotation.RetentionPolicy
中進行了枚舉,包括:
- SOURCE 存在源碼中,編譯階段被丟棄
- CLASS 默認級別,存在類文件中,VM加載時將丟棄
- RUNTIME 運行時,存在於運行時,可以通過反射API獲取
而處理注解的方式通常為以下幾種情況:
- 使用
javax.annotation.processing
處理器API,在編譯前對注解進行處理,所有生存期的注解都能被處理; - 使用字節碼處理框架如bcel對class文件進行讀取並處理其中的注解,此時SOURCE級注解已經不存在;
- 使用
java.lang.instrument
設備API,獲得class文件並處理其中的注解,此時SOURCE級注解已經不存在; - 使用
java.lang.annotation
API,獲取反射對象的注解並處理,此時SOURCE&CLASS級注解已經不存在。這是最常用的方式。
《Java核心技術 卷2》第10章節對上面方式都進行了介紹和基本實現。
元注解@Inherited
如果注解類型使用了@Inherited
,聲明該注解的父類會將其注解繼承給子類。
下面用例子說明:
首先是注解類型Country
用來聲明人所屬國家。
@Inherited
@Retention(RetentionPolicy.RUNTIME)//因為是運行時測試,所以需要讓注解在運行時保留
public @interface Country {
String name();
}
然后是Chinese
表示中國人,其所屬國家為China
。
/**
* 中國人
*/
@Country(name = "China")
public class Chinese {
}
子類SiChuanese
表示四川人。
/**
* 四川人
*/
public class SiChuanese extends Chinese {
}
測試SiChuanese
是否從其父類Chinese
繼承了注解Country
:
public class CountryTest {
@Test
public void testCountry(){
Assert.assertEquals("China",SiChuanese.class.getAnnotation(Country.class).name());//測試通過
}
}
單值注解
可能好奇使用Spring框架時為什么當只需要一個元素時,可以省略元素名。如在Spring中:
@RequestMapping("/index")
這其實是注解的語法部分,叫做單值注解,特殊的value
元素,當只指定該元素值時,可以省略元素名和等號。
參考《Java核心技術 卷2》 744頁
其實質等於:
@RequestMapping(value="/index")
又因為Spring使用自定義注解@AliasFor
聲明了注解別名。因此等價於下面的注解:
@RequestMapping(name="/index")