註解的原理又是怎麼一回事

超人汪小建發表於2019-03-03

Java內建的註解以及自定義一個註解大家都比較熟悉的了,現在來看看註解實現的原理,看看Java的體系下面是如何對註解的支援的。

在討論前先看一個自定義註解的例子,自定義實現這樣一個註解:通過@Test向某類注入一個字串,通過@TestMethod向某個方法注入一個字串。

① 建立Test註解,宣告作用於類並保留到執行時,預設值為default。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {
    String value() default "default";
}複製程式碼

② 建立TestMethod註解,宣告作用於方法並保留到執行時。

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface TestMethod {
    String value();
}複製程式碼

③測試類,執行後輸出default和tomcat-method兩個字串,因為@Test沒有傳入值,所以輸出了預設值,而@TestMethod則輸出了注入的字串。

@Test()
public class AnnotationTest {
    @TestMethod("tomcat-method")
    public void test(){
    }
    public static void main(String[] args){
        Test t = AnnotationTest.class.getAnnotation(Test.class);
        System.out.println(t.value());
        TestMethod tm = null;
        try {
            tm = AnnotationTest.class.getDeclaredMethod("test",null).getAnnotation(TestMethod.class);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(tm.value());
    }
}複製程式碼

對於註解Test,如果對AnnotationTest類進行註解,則執行時可以通過AnnotationTest.class.getAnnotation(Test.class)獲取註解宣告的值,從上面的句子就可以看出,它是從class結構中獲取出Test註解的,所以肯定是在某個時候註解被加入到class結構中去了。

@Test("test") 
public class AnnotationTest { 
    public void test(){ 
    } 
}複製程式碼

從java原始碼到class位元組碼是由編譯器完成的,編譯器會對java原始碼進行解析並生成class檔案,而註解也是在編譯時由編譯器進行處理,編譯器會對註解符號處理並附加到class結構中,根據jvm規範,class檔案結構是嚴格有序的格式,唯一可以附加資訊到class結構中的方式就是儲存到class結構的attributes屬性中。我們知道對於類、欄位、方法,在class結構中都有自己特定的表結構,而且各自都有自己的屬性,而對於註解,作用的範圍也可以不同,可以作用在類上,也可以作用在欄位或方法上,這時編譯器會對應將註解資訊存放到類、欄位、方法自己的屬性上。

在我們的AnnotationTest類被編譯後,在對應的AnnotationTest.class檔案中會包含一個RuntimeVisibleAnnotations屬性,由於這個註解是作用在類上,所以此屬性被新增到類的屬性集上。即Test註解的鍵值對value=test會被記錄起來。而當JVM載入AnnotationTest.class檔案位元組碼時,就會將RuntimeVisibleAnnotations屬性值儲存到AnnotationTest的Class物件中,於是就可以通過AnnotationTest.class.getAnnotation(Test.class)獲取到Test註解物件,進而再通過Test註解物件獲取到Test裡面的屬性值。

這裡可能會有疑問,Test註解物件是什麼?其實註解被編譯後的本質就是一個繼承Annotation介面的介面,所以@Test其實就是public interface Test extends Annotation,當我們通過AnnotationTest.class.getAnnotation(Test.class)呼叫時,JDK會通過動態代理生成一個實現了Test介面的物件,並把將RuntimeVisibleAnnotations屬性值設定進此物件中,此物件即為Test註解物件,通過它的value()方法就可以獲取到註解值。

Java註解實現機制的整個過程如上面所示,它的實現需要編譯器和JVM一起配合。

====廣告時間,可直接跳過====

鄙人的新書《Tomcat核心設計剖析》已經在京東預售了,有需要的朋友可以到 item.jd.com/12185360.ht… 進行預定。感謝各位朋友。

=========================

歡迎關注:

註解的原理又是怎麼一回事

相關文章