重新認識Java註解

重新認識Java註解

今天Debug看源碼的時候,無意間看到這麼個東西

圖1

首先承認我的無知,看到這個我很驚詫。

也勾起了我的好奇心,於是有了這篇認知記錄。

下面就來重新認識下註解吧!

註解的本質

關於運行時註解的信息,會在.class文件中,並且最終以運行時數據結構存儲在方法區,也知道我們是可以通過Class對象或者Method對象,來獲取其相應的註解信息的。

不過確實沒有意識到,或者說根本就沒有去猜想其背後的實現,也許是直接使用來解析註解的機會比較少吧。

現在才認識到,原來我們定義的註解, 最終使用的時候,都是以一個代理類的方式與相應的Class或者Method對象綁定到一起。

所有的註解,其實都是接口Annotation子接口,而每一個@interface的聲明,最後其實就是一個普通的interface罷了!下面請看

public @interface AnnotationDemo {

    int value();

    int name ();

}



public interface com.example.demo.anno.AnnotationDemo extends java.lang.annotation.Annotation {
  	
  	public abstract int value();

 	public abstract int name();
}

從上面對一個註解類的反編譯結果就能看出來,它其實就是一個普通的接口類

從接口到實例

我們是如何查找到一個類定義的那些註解然後去使用呢?

答案是:從Class對象中,我們可以獲取所有的信息

一個Class 的所有Annotation代理類被封裝到一個私有靜態類AnnotationData

private static class AnnotationData {
    // 一個Map 映射 具體的Annotation Class 和其代理類對象
    final Map<Class<? extends Annotation>, Annotation> annotations;
    final Map<Class<? extends Annotation>, Annotation> declaredAnnotations;

    // Value of classRedefinedCount when we created this AnnotationData instance
    final int redefinedCount;

    AnnotationData(Map<Class<? extends Annotation>, Annotation> annotations,
                   Map<Class<? extends Annotation>, Annotation> declaredAnnotations,
                   int redefinedCount) {
        this.annotations = annotations;
        this.declaredAnnotations = declaredAnnotations;
        this.redefinedCount = redefinedCount;
    }
}

Class類中有一個成員變量

    private volatile transient AnnotationData annotationData;

而具體的創建動態代理對象的操作,則是懶加載的方式

  public Annotation[] getAnnotations() {
        // 調用 Class#annotationData方法
        return AnnotationParser.toArray(annotationData().annotations);
  }
private AnnotationData annotationData() {
    while (true) { // retry loop
        AnnotationData annotationData = this.annotationData;
        int classRedefinedCount = this.classRedefinedCount;
        // 如果已經初始化,並且這個類的redefinedCount和創建此AnnotationData對象時一致
        // 則無需重新創建AnnotationData對象
        if (annotationData != null &&
            annotationData.redefinedCount == classRedefinedCount) {
            return annotationData;
        }
        // null or stale annotationData -> optimistically create new instance
        // 爲null 或者已經過時了,創建一個新的實例
        AnnotationData newAnnotationData = createAnnotationData(classRedefinedCount);
        // try to install it
        // 使用Unsafe CAS去更新字段 annotationData,直至成功
        if (Atomic.casAnnotationData(this, annotationData, newAnnotationData)) {
            // successfully installed new AnnotationData
            return newAnnotationData;
        }
    }
}

創建AnnotationData時,是通過一些native方法,獲取類相關的annotation元信息的byte[]數組表示,然後解析出註解接口對應的Class對象,最後去通過Jdk Dynamic Proxy動態代理來創建對象

public static Annotation annotationForMap(final Class<? extends Annotation> var0, final Map<String, Object> var1) {
    return (Annotation)AccessController.doPrivileged(new PrivilegedAction<Annotation>() {
        public Annotation run() {
            // JDK動態代理
            return (Annotation)Proxy.newProxyInstance(var0.getClassLoader(), new Class[]{var0}, new AnnotationInvocationHandler(var0, var1));
        }
    });
}

這樣最終就創建了一個註解接口的代理類

總結

註解類就是一個普通的接口類,最終在使用時,會創建相應的代理對象,用來獲取定義在註解上的一些元數據信息。

爲什麼要用接口?

我的理解是,接口簡單、簡潔,所有的方法都是抽象方法,屬性都是靜態常量,而我們的添加在註解上的一些信息,通常都是一些值,並不需要方法體來去做些什麼。

不過使用接口來實現註解,就會有個問題,接口的字段都是靜態常量,不能修改,所以註解裏定義的都是方法,而動態代理類就是爲了能在運行時,調用註解定義的方法,就能獲取我們定義在註解上的值。

到這裏,對註解的實現已經有了一個大概的認識,不過一些細節,並沒有深究,能力有限,待需要時,有機會和能力再去深究。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章