深刻學習java註解

對於java的註解, 本身已經使用了至關長的時間, spring中對註解的使用無處不在,但對它的瞭解並不深刻, 本週對註解進行了較爲深刻的學習。java

什麼是註解

Java 註解用於爲 Java 代碼提供元數據。做爲元數據,註解不直接影響你的代碼執行,但也有一些類型的註解實際上能夠用於這一目的。Java 註解是從 Java5 開始添加到 Java 的。

上面的是java註解的官方解釋,相信看了之後原本明白的仍然明白,可是不明白的卻仍是不明白。git

我感受註解能夠簡單理解爲是一個標籤, 如果在類或屬性等上面有什麼標籤,咱們就須要對其進行某些處理,如何處理呢?別急,後面會說。github

註解的本質

public @interface TestAnnotion {
}

一個最簡單的註解定義方式就像上面同樣, 和接口很像對不對,只是多了一個@符號,註解其實是一種繼承自接口java.lang.annotation.Annotation的特殊接口,jdk文檔有以下說法spring

An annotation type declaration specifies a new annotation type, a special kind of interface type. To distinguish an annotation type declaration from a normal interface declaration, the keyword interface is preceded by an at-sign (@)
……
The direct superinterface of every annotation type is java.lang.annotation.Annotation.

註釋類型聲明指定一種新的註釋類型,一種特殊的接口類型。要將註釋類型聲明與普通接口聲明區分開來,關鍵字接口前面有at符號(@)
……
每一個註釋類型的直接上接口是java.lang.annotation.annotation。sql

因此上面定義的TestAnnotion,能夠理解成這樣數據庫

public interface TestAnnotion extends Annotation {
}

這篇文章這篇文章
經過反編譯給咱們了更直觀的展現這個結論。segmentfault

自定義註解

想要自定義註解就確定要了解元註解網絡

元註解

  • 元註解顧名思義咱們能夠理解爲註解的註解,它是做用在註解中,方便咱們使用註解實現想要的功能。元註解分別有@Retention、 @Target、 @Document、 @Inherited和@Repeatable(JDK1.8加入)五種。

@Retention

  • Retention英文意思有保留、保持的意思,它表示註解存在階段是保留在源碼(編譯期),字節碼(類加載)或者運行期(JVM中運行)。在@Retention註解中使用枚舉RetentionPolicy來表示註解保留時期
  • @Retention(RetentionPolicy.SOURCE),註解僅存在於源碼中,在class字節碼文件中不包含
  • @Retention(RetentionPolicy.CLASS), 默認的保留策略,註解會在class字節碼文件中存在,但運行時沒法得到
  • @Retention(RetentionPolicy.RUNTIME), 註解會在class字節碼文件中存在,在運行時能夠經過反射獲取到
  • 若是咱們是自定義註解,則經過前面分析,咱們自定義註解若是隻存着源碼中或者字節碼文件中就沒法發揮做用,而在運行期間能獲取到註解才能實現咱們目的,因此自定義註解中確定是使用 @Retention(RetentionPolicy.RUNTIME)

@Target

  • Target的英文意思是目標,這也很容易理解,使用@Target元註解表示咱們的註解做用的範圍就比較具體了,能夠是類,方法,方法參數變量等,一樣也是經過枚舉類ElementType表達做用類型
  • @Target(ElementType.TYPE) 做用接口、類、枚舉、註解
  • @Target(ElementType.FIELD) 做用屬性字段、枚舉的常量
  • @Target(ElementType.METHOD) 做用方法
  • @Target(ElementType.PARAMETER) 做用方法參數
  • @Target(ElementType.CONSTRUCTOR) 做用構造函數
  • @Target(ElementType.LOCAL_VARIABLE)做用局部變量
  • @Target(ElementType.ANNOTATION_TYPE)做用於註解(@Retention註解中就使用該屬性)
  • @Target(ElementType.PACKAGE) 做用於包
  • @Target(ElementType.TYPE_PARAMETER) 做用於類型泛型,即泛型方法、泛型類、泛型接口 (jdk1.8加入)
  • @Target(ElementType.TYPE_USE) 類型使用.能夠用於標註任意類型除了 class (jdk1.8加入)
  • 通常比較經常使用的是ElementType.TYPE類型

@Documented

  • Document的英文意思是文檔。它的做用是可以將註解中的元素包含到 Javadoc 中去。

@Inherited

  • Inherited的英文意思是繼承,可是這個繼承和咱們平時理解的繼承大同小異,一個被@Inherited註解了的註解修飾了一個父類,若是他的子類沒有被其餘註解修飾,則它的子類也繼承了父類的註解。

@Repeatable

  • Repeatable的英文意思是可重複的。顧名思義說明被這個元註解修飾的註解能夠同時做用一個對象屢次,可是每次做用註解又能夠表明不一樣的含義。

實現spring data jpa的@Entity(僞)

咱們知道 spring data jpa 中 若是給某個類加上@Entity的註解,spring就會爲咱們建立相應的數據表,app

接下來咱們就實現一個註解:他會生成建立一個數據表的sql,而後打印出來(執行也是的原理,咱就不執行了), 要實現這個功能,須要和反射相結合,如果還未學習過反射能夠先經過這篇文章學習一下。ide

Entity.java

@Target(ElementType.TYPE) // 做用於類
@Retention(RetentionPolicy.RUNTIME)
public @interface Entity {

}

Member.java

@Entity
public class Member {
    String name;
    Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

TableCreator.java

public class TableCreator {
    public static void
    main(String[] args) throws Exception {
        // 獲取Member的實例變量
        Class<?> cl = Member.class;
        // 查找該類上是否有相應的註解
        Entity entity = cl.getAnnotation(Entity.class);
        if (entity == null) {
            System.out.println(
                    "該類沒有Entity註解");
            return;
        }
        // 獲取數據庫的名
        String tableName = cl.getName().toLowerCase();

        // 定義像對象的屬性
        List<String> columnDefs = new ArrayList<>();
        for (Field field : cl.getDeclaredFields()) {
            String columnName = field.getName().toLowerCase();

            String columnDef = columnName + " varchar(50)";
            columnDefs.add(columnDef);
        }

        // 構造sql語句
        StringBuilder createCommand = new StringBuilder(
                "create table " + tableName + "(");
        for (String columnDef : columnDefs)
            createCommand.append("\n    ").append(columnDef).append(",");
        String tableCreate = createCommand.substring(
                0, createCommand.length() - 1) + ");";
        System.out.println("生成的sql語句爲:\n" + tableCreate);
    }

}

image.png
像上面這種處理提取和處理 Annotation 的代碼統稱爲 APT(Annotation Processing Tool)
#### 註解成員變量

咱們能夠在註解中設置成員變量,形如

@Target(ElementType.TYPE) // 做用於類
@Retention(RetentionPolicy.RUNTIME)
public @interface Entity {

  // name 值必須賦值, 若不設置默認值則必須在使用處賦值
  String name() default "";
}

賦值方式
image.png

同時,還有一點須要注意的是,若是你在註解中定義了名爲 value 的元素,而且在使用該註解時,value 爲惟一一個須要賦值的元素,你就不須要使用鍵—值對的語法,你只須要在括號中給出 value元素的值便可。這能夠應用於任何合法類型的元素。這也限制了你必須將元素命名爲 value,不過在上面的例子中,這樣的註解語句也更易於理解:

@Target(ElementType.TYPE) // 做用於類
@Retention(RetentionPolicy.RUNTIME)
public @interface Entity {

    //value 爲惟一一個須要賦值的元素,你就不須要使用名—值對的語法
    String value();
    String test() default "";
}

image.png
但若不止一個須要賦值的變量,則value也須要鍵值對的形式
image.png

獲取成員變量的值

直接經過獲取便可

// 獲取Member的實例變量
      Class<?> cl = Member.class;
      // 查找該類上是否有相應的註解
      Entity entity = cl.getAnnotation(Entity.class);
      // 獲取註解中的成員變量值
      String name = entity.value();

java內置的註解

除了上面幾種用於定義註解的註解外,java還爲咱們提供了另外五個註解,Java 5 引入了前三種定義在 java.lang 包中的註解:

  • @Override:表示當前的方法定義將覆蓋基類的方法。若是你不當心拼寫錯誤,或者方法簽名被錯誤拼寫的時候,編譯器就會發出錯誤提示。
  • @Deprecated:若是使用該註解的元素被調用,編譯器就會發出警告信息。
  • @SuppressWarnings:關閉不當的編譯器警告信息。
  • @SafeVarargs:在 Java 7 中加入用於禁止對具備泛型varargs參數的方法或構造函數的調用方發出警告。
  • @FunctionalInterface:Java 8 中加入用於表示類型聲明爲函數式接口

總結

對於註解咱們能夠理解爲一個標籤,本質上是一個特殊的接口,他的功能主要有以下幾點:

  • 提供信息給編譯器: 編譯器能夠利用註解來檢測出錯誤或者警告信息,打印出日誌。
  • 編譯階段時的處理: 軟件工具能夠用來利用註解信息來自動生成代碼、文檔或者作其它相應的自動處理。
  • 運行時處理: 某些註解能夠在程序運行的時候接受代碼的提取,自動作相應的操做。
  • 正如官方文檔的那句話所說,註解可以提供元數據,轉帳例子中處理獲取註解值的過程是咱們開發者直接寫的註解提取邏輯,處理提取和處理 Annotation 的代碼統稱爲 APT(Annotation Processing Tool)

參考文章

java註解是怎麼實現的?
Java 註解徹底解析
第二十三章 註解

版權聲明

本文保留全部權利,版權歸河北工業大學夢雲智軟件開發團隊全部。未經團隊及做者事先書面贊成,您不得以任何方式將本文內容進行商業性使用或經過信息網絡傳播本文內容

相關文章
相關標籤/搜索