用一個詞就能夠描述註解,那就是元數據,即一種描述數據的數據。因此,能夠說註解就是源代碼的元數據。好比,下面這段代碼:java
@Override
public String toString() {
return "This is String Representation of current object.";
}
複製代碼
上面的代碼中,重寫了toString()方法並使用了@Override註解。程序員
事實上,@Override告訴編譯器這個方法是一個重寫方法(描述方法的元數據),若是父類中不存在該方法,編譯器便會報錯,提示該方法沒有重寫父類中的方法。若是不當心拼寫錯誤,例如將toString()寫成了toStrring(){double r},並且也沒有使用@Override註解,那程序依然能編譯運行,但運行結果會和指望的大不相同。架構
Annotation是一種應用於類、方法、參數、變量、構造器及包聲明中的特殊修飾符。它是一種由JSR-175標準選擇用來描述元數據的一種工具。使用註解有助於閱讀程序。app
使用Annotation以前(甚至在使用以後),XML被普遍的應用於描述元數據。不知什麼時候開始一些應用開發人員和架構師發現XML的維護愈來愈糟糕了。他們但願使用一些和代碼緊耦合的東西,而不是像XML那樣和代碼是鬆耦合的(在某些狀況下甚至是徹底分離的)代碼描述。若是你在Google中搜索「XML vs. annotations」,會看到許多關於這個問題的辯論。最有趣的是XML配置其實就是爲了分離代碼和配置而引入的。上述兩種觀點可能會讓你很疑惑,二者觀點彷佛構成了一種循環,但各有利弊。下面咱們經過一個例子來理解這二者的區別。ide
假如你想爲應用設置不少的常量或參數,這種狀況下,XML是一個很好的選擇,由於它不會同特定的代碼相連。若是你想把某個方法聲明爲服務,那麼使用Annotation會更好一些,由於這種狀況下須要註解和方法緊密耦合起來,開發人員也必須認識到這點。函數
另外一個很重要的因素是Annotation定義了一種標準的描述元數據的方式。在這以前,開發人員一般使用他們本身的方式定義元數據。例如,使用標記interfaces,註釋,transient關鍵字等等。每一個程序員按照本身的方式定義元數據,而不像Annotation這種標準的方式。工具
當註解標註到某個類或者方法或者某個成員變量或者某個輸入參數上的時候,必定有一個對應的機制來對註解標註的類、方法、成員變量和參數進行某些處理。spa
編寫Annotation很是簡單,註解自己也能夠說是一個類。能夠將Annotation的定義同接口的定義進行比較。看個例子,標準的註解@Override:code
package java.lang;
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
複製代碼
能夠看見,@Override註解彷佛什麼都沒作,那它是如何檢查在父類中有一個同名的函數?@Override註解的定義僅僅是元數據,不包含業務邏輯,而實現業務邏輯的就是註解的用戶。繼承
對於@Override註解,它的用戶就是JVM虛擬機,工做在字節碼層面,在編譯階段進行檢查,其處理機制主要是JVM內部處理。
再例如Spring中的 @Service 註解,Spring在啓動IOC容器的時候會對每一個類進行掃描,把全部標註@Component及其子註解如@Service的類進行Bean處理。
JDK5.0版本在 java.lang.annotation提供了四種元註解,專門註解其餘的註解:
@Documented – 是否將註解信息添加在java文檔中
@Retention – 定義該註解的生命週期
@Target –註解用於什麼地方
@Inherited – 是否容許子類繼承該註解
@Retention 註解定義該註解的生命週期,在什麼階段丟棄,
RetentionPolicy.SOURCE – 在編譯階段丟棄。這些註解在編譯結束以後就再也不有任何意義,因此它們不會寫入字節碼。@Override, @SuppressWarnings都屬於這類註解。
RetentionPolicy.CLASS – 在類加載的時候丟棄,在字節碼文件的處理中有用。註解默認使用這種方式。
RetentionPolicy.RUNTIME – 始終不會丟棄,運行期也保留該註解,所以可使用反射機制讀取該註解的信息。自定義的註解一般使用這種方式。
@Target – 表示該註解用於什麼地方。若是不明確指出,該註解能夠放在任何地方。屬性的註解是兼容的。
ElementType.TYPE:用於描述類、接口或enum聲明
ElementType.FIELD:用於描述實例變量
ElementType.METHOD
ElementType.PARAMETER
ElementType.CONSTRUCTOR
ElementType.LOCAL_VARIABLE
ElementType.ANNOTATION_TYPE 另外一個註釋
ElementType.PACKAGE 用於記錄java文件的package信息
那麼,註解的內部究竟是如何定義的呢?Annotations只支持基本類型、String及枚舉類型。註釋中全部的屬性被定義成方法,並容許提供默認值。
定義一個 @AuthCheck 註解,做用在SpringMVC controller的方法上。
package com.liuning.sys.auth;
import static java.lang.annotation.ElementType.METHOD;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Documented
@Target(METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthCheck {
String value() default "";
}
複製代碼
註解中只有一個屬性,能夠直接命名爲「value」,使用時無需再標明屬性名。
在Spring的攔截器HandlerInterceptor中實現@AuthCheck註解的業務邏輯,這裏使用了反射機制。
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HandlerMethod hm = (HandlerMethod) handler;
AuthCheck ac = hm.getMethodAnnotation(AuthCheck.class);
if (ac != null) {
if (ac.value() == "Login") {
//進行業務邏輯操做
}
}
return true;
}
複製代碼
下面的例子演示瞭如何使用上面的註解。使用了該註解的方法在用戶調用的時候會須要用戶已登陸。
@PostMapping("/list")
@AuthCheck(value = "Login")
public List<User> getUserList(@RequestBody @Valid User user) {
return userService.getUserList(user);
}
複製代碼