Java註解學習—基本概念
基礎
定義
註解,Annotation,又被稱為元資料,是一種程式碼級別的說明。有一種理解的方式:元資料是用於描述資料自身的資料,而註解就是程式碼的元資料,包含了程式碼自身的資訊。
註解是在JDK1.5引入的,與類、介面、列舉在同一個層次。在各種各樣的框架中得到了廣泛的應用。
註解的作用域
註解可以宣告在類、欄位、方法、區域性變數、方法引數等前面。而從JDK8開始,型別註解是可以放在程式碼的任何為的。註解可以被編譯進class檔案中,也可以只存在於原始碼中。
註解通常可以用於:
編寫文件:通過程式碼裡的標識的元資料生成文件,這是比較常見的最早的註解;
程式碼分析:通過程式碼標識的元資料對程式碼進行分析(通常會進行反射呼叫),取代了配置檔案的作用,從而可以影響執行時程式的執行;
編譯檢查:通過Override等常見註解可以讓編譯器進行基本的編譯檢查。
註解的意義
被註解的程式碼並不會直接被註解影響,而是會向第三方系統提供關於自己的資訊(Spring的註解會在程式執行時,由Spring容器進行控制)。註解本身沒有任何的邏輯,不會影響被註解的程式碼,所以它只有對它的”消費器”[1]才具有實際的意義。
Java自帶的註解(元註解)會被JVM所消費,而其他的,Spring中的Controller註解,則會被Spring容器所消費。
也有這樣一種理解,從某種角度上來說,可以把註解看做是XML元素,該元素可以有預定義屬性,屬性值可以在宣告元素時定義。
註解用法分析
註解語法
@<註解名>(<成員名1>=<成員值1>,<成員名2>=<成員值2>,…)
- @作為字首,用於向編譯器表示這是一個註解元素;
- <註解名>為必須存在的,表示該註解的名稱
- 註解的值可以有多種情況:不指定成員值;指定一個成員值(或只有一個成員值)其他採用預設值;鍵值對指定成員值
這裡列舉springmvc中常見的註解
在一個Spring的Controller中,可以有多種註解方式
@RequestMapping("/index.html") //一個成員值
@RequestMapping(value="index.html",headers="Accept=application/json")
@ResponseBody //多個註解作用在一個作用域上
@Controller //沒有屬性
此外,多個註解可以同時存在在一個作用域上。
註解分類
註解產生類別
內建註解和自定義註解
內建註解是java自帶的幾種註解,如Override等;
自定義註解是自定義註解。
註解執行時機制
- 原始碼註解
- 編譯時註解
- 執行時註解
一般來說,只有執行時註解對我們的用處比較大,可以在執行時,通過判斷這個class或是方法有沒有註解,從而反射呼叫該內容。也就是說,註解在執行時呼叫的時候,往往需要用自己的程式判斷對應的類是否有註解,根據不同的結果進行不同的方法處理。
自定義註解實現
定義註解類
自定義註解必須:
- 以@interface開頭
- 成員必須以無引數無異常方式宣告
- 用default為成員指定預設值
- 成員型別包括原始型別、String、Class、Annotation、Enumeration
- 成員如果只有一個,成員名必須為value()
下面是一個自定義註解實現
package com.javastudy.annotation;
import java.lang.annotation.*;
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Description {
String desc();
String author();
int age() default 18;
}
在這個例子中,我們可以看到在註解上還有註解,這些註解被稱為元註解。
元註解
元註解是用來修飾註解的註解,是註解的註解。
@Target
用於描述註解的使用範圍(即:被描述的註解可以用在什麼地方)
@Target說明了Annotation所修飾的物件範圍:Annotation可被用於packages、types(類、介面、列舉、Annotation型別)、型別成員(方法、構造方法、成員變數、列舉值)、方法引數和本地變數(如迴圈變數、catch引數)。在Annotation型別的宣告中使用了target可更加明晰其修飾的目標。
ElementType.CONSTRUCTOR:用於描述構造器
ElementType.FIELD:用於描述域
ElementType.LOCAL_VARIABLE:用於描述區域性變數
ElementType.METHOD:用於描述方法
ElementType.PACKAGE:用於描述包
ElementType.PARAMETER:用於描述引數
ElementType.TYPE:用於描述類、介面(包括註解型別) 或enum宣告
@Retention
定義了該Annotation被保留的時間長短:某些Annotation僅出現在原始碼中,而被編譯器丟棄;而另一些卻被編譯在class檔案中;編譯在class檔案中的Annotation可能會被虛擬機器忽略,而另一些在class被裝載時將被讀取(請注意並不影響class的執行,因為Annotation與class在使用上是被分離的)。
取值有
RetentionPoicy.SOURCE:在原始檔中有效(即原始檔保留)
RetentionPoicy.CLASS:在class檔案中有效(即class保留)
RetentionPoicy.RUNTIME:在執行時有效(即執行時保留)
@Documented
@Documented用於描述其它型別的annotation應該被作為被標註的程式成員的公共API,因此可以被例如javadoc此類的工具文件化。Documented是一個標記註解,沒有成員。
@Inherited
@Inherited 元註解是一個標記註解,@Inherited闡述了某個被標註的型別是被繼承的。如果一個使用了@Inherited修飾的annotation型別被用於一個class,則這個annotation將被用於該class的子類。
下圖是個註解知識點思維導圖[2]:
同時,知乎上,也有個對註解程式碼級的詳細解釋,見[3]