所謂元註解其實就是能夠註解到別的註解上的註解,被註解的註解稱之爲組合註解,組合註解具有其上元註解的功能.html
在JDK中提供了4個標準的用來對註解類型進行註解的註解類,咱們稱之爲 meta-annotation(元註解),他們分別是:java
咱們可使用這4個元註解來對咱們自定義的註解類型進行註解.數組
Target註解的做用是:描述註解的使用範圍(即被修飾的註解能夠用在什麼地方).bash
Target註解用來講明那些被它所註解的註解類可修飾的對象範圍:註解能夠用於修飾 packages、types(類、接口、枚舉、註解類)、類成員(方法、構造方法、成員變量、枚舉值)、方法參數和本地變量(如循環變量、catch參數),在定義註解類時使用了@Target 可以更加清晰的知道它可以被用來修飾哪些對象,它的取值範圍定義在ElementType 枚舉中.
源碼ide
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
ElementType[] value();
}
複製代碼
ElementType工具
public enum ElementType {
TYPE, // 類、接口、枚舉類
FIELD, // 成員變量(包括:枚舉常量)
METHOD, // 成員方法
PARAMETER, // 方法參數
CONSTRUCTOR, // 構造方法
LOCAL_VARIABLE, // 局部變量
ANNOTATION_TYPE, // 註解類
PACKAGE, // 可用於修飾:包
TYPE_PARAMETER, // 類型參數,JDK 1.8 新增
TYPE_USE // 使用類型的任何地方,JDK 1.8 新增
}
複製代碼
Reteniton註解的做用是:描述註解保留的時間範圍(即:被描述的註解在它所修飾的類中能夠被保留到什麼時候).學習
Reteniton註解用來限定那些被它所註解的註解類在註解到其餘類上之後,可被保留到什麼時候,一共有三種策略,定義在RetentionPolicy枚舉中.
RetentionPolicy測試
public enum RetentionPolicy {
SOURCE, // 源文件保留
CLASS, // 編譯期保留,默認值
RUNTIME // 運行期保留,可經過反射去獲取註解信息
}
複製代碼
生命週期長度 SOURCE < CLASS < RUNTIME ,前者能做用的地方後者必定也能做用。若是須要在運行時去動態獲取註解信息,那隻能用 RUNTIME 註解;若是要在編譯時進行一些預處理操做,好比生成一些輔助代碼(如 ButterKnife),就用 CLASS註解;若是隻是作一些檢查性的操做,好比 @Override 和 @SuppressWarnings,則可選用 SOURCE 註解。ui
Documented註解的做用是:描述在使用 javadoc 工具爲類生成幫助文檔時是否要保留其註解信息。
這裏驗證@Documented的做用,咱們建立一個自定義註解:this
@Documented
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface MyDocumentedt {
public String value() default "這是@Documented註解爲文檔添加的註釋";
}
複製代碼
而後建立一個測試類,在方法和類上都加入自定義的註解
@MyDocumentedt
public class MyDocumentedTest {
/**
* 測試 document
* @return String the response
*/
@MyDocumentedt
public String test(){
return "sdfadsf";
}
}
複製代碼
打開java文件所在的目錄下,打開命令行輸入:
javac .\MyDocumentedt.java .\MyDocumentedTest.java
複製代碼
javadoc -d doc .\MyDocumentedTest.java .\MyDocumentedt.java
複製代碼
index.html
,能夠發如今類和方法上都保留了 MyDocumentedt 註解信息。
Inherited註解的做用是:使被它修飾的註解具備繼承性(若是某個類使用了被@Inherited修飾的註解,則其子類將自動具備該註解)。
經過代碼來進行驗證,建立一個自定義註解
@Target({ElementType.TYPE})
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface MyInherited {
}
複製代碼
驗證
@MyInherited
public class A {
public static void main(String[] args) {
System.out.println(A.class.getAnnotation(MyInherited.class));
System.out.println(B.class.getAnnotation(MyInherited.class));
System.out.println(C.class.getAnnotation(MyInherited.class));
}
}
class B extends A{
}
class C extends B{
}
複製代碼
執行main方法,從控制檯中能夠看到打印的信息
重複註解:即容許在同一申明類型(類,屬性,或方法)前屢次使用同一個類型註解。
在java8 之前,同一個程序元素前最多隻能有一個相同類型的註解;若是須要在同一個元素前使用多個相同類型的註解,則必須使用註解「容器」。 java8以前的作法
public @interface Roles {
Role[] roles();
}
複製代碼
public @interface Roles {
Role[] value();
}
複製代碼
public class RoleAnnoTest {
@Roles(roles = {@Role(roleName = "role1"), @Role(roleName = "role2")})
public String doString(){
return "";
}
}
複製代碼
java8以後增長了重複註解,使用方式以下:
public @interface Roles {
Role[] value();
}
複製代碼
@Repeatable(Roles.class)
public @interface Role {
String roleName();
}
複製代碼
public class RoleAnnoTest {
@Role(roleName = "role1")
@Role(roleName = "role2")
public String doString(){
return "";
}
}
複製代碼
不一樣的地方是,建立重複註解 Role 時,加上@Repeatable,指向存儲註解 Roles,在使用時候,直接能夠重複使用 Role 註解。從上面例子看出,java 8裏面作法更適合常規的思惟,可讀性強一點。可是,仍然須要定義容器註解。
兩種方法得到的效果相同。重複註解只是一種簡化寫法,這種簡化寫法是一種假象:多個重複註解其實會被做爲「容器」註解的 value 成員的數組元素處理。
Java8 爲 ElementType 枚舉增長了TYPE_PARAMETER、TYPE_USE兩個枚舉值,從而可使用 @Target(ElementType_TYPE_USE) 修飾註解定義,這種註解被稱爲類型註解,能夠用在任何使用到類型的地方。
在 java8 之前,註解只能用在各類程序元素(定義類、定義接口、定義方法、定義成員變量…)上。從 java8 開始,類型註解能夠用在任何使用到類型的地方。
TYPE_PARAMETER:表示該註解能寫在類型參數的聲明語句中。
TYPE_USE:表示註解能夠再任何用到類型的地方使用,好比容許在以下位置使用:
public class TypeUserTest {
public static void main(String[] args) {
String str = "str";
Object obj = (@isNotNull Object) str;
}
}
@Target(ElementType.TYPE_USE)
@interface isNotNull{
}
複製代碼
這種無處不在的註解,可讓編譯器執行更嚴格的代碼檢查,從而提升程序的健壯性。
經過上面的學習已經初步瞭解了元註解是怎麼一回事,下面擼一個自定義註解來融會貫通. 自定義註解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Name {
public String value() default "";
}
複製代碼
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Sex {
public enum GenderType {
Male("男"),
Female("女");
private String genderStr;
private GenderType(String arg0) {
this.genderStr = arg0;
}
@Override
public String toString() {
return genderStr;
}
}
GenderType gender() default GenderType.Male;
}
複製代碼
使用自定義註解的實體類
@Data
public class User {
@Name(value = "wtj")
public String name;
public String age;
@Sex(gender = Sex.GenderType.Male)
public String sex;
}
複製代碼
測試
public class AnnotionUtils {
public static String getInfo(Class<?> cs){
String result = "";
//經過反射獲取全部聲明的字段
Field[] declaredFields = cs.getDeclaredFields();
//獲取全部字段
for (Field field : declaredFields){
if(field.isAnnotationPresent(Name.class)){
//獲取程序元素上的註解
Name annotation = field.getAnnotation(Name.class);
String value = annotation.value();
result += (field.getName() + ":" + value + "\n");
}
if(field.isAnnotationPresent(Sex.class)){
Sex annotation = field.getAnnotation(Sex.class);
String value = annotation.gender().name();
result += (field.getName() + ":" + value + "\n");
}
}
return result;
}
public static void main(String[] args){
String info = getInfo(User.class);
System.out.println(info);
}
}
複製代碼
main方法運行後就能夠在控制檯中看到使用註解時傳入的數據了. 上面就是一個簡單的註解使用的demo,固然,在實際工做中使用的會相對複雜,這就須要咱們根據業務需求及代碼需求進行封裝和使用自定義註解了.