重新認識Java註解
今天Debug看源碼的時候,無意間看到這麼個東西
首先承認我的無知,看到這個我很驚詫。
也勾起了我的好奇心,於是有了這篇認知記錄。
下面就來重新認識下註解吧!
註解的本質
關於運行時註解的信息,會在.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));
}
});
}
這樣最終就創建了一個註解接口的代理類
總結
註解類就是一個普通的接口類,最終在使用時,會創建相應的代理對象,用來獲取定義在註解上的一些元數據信息。
爲什麼要用接口?
我的理解是,接口簡單、簡潔,所有的方法都是抽象方法,屬性都是靜態常量,而我們的添加在註解上的一些信息,通常都是一些值,並不需要方法體來去做些什麼。
不過使用接口來實現註解,就會有個問題,接口的字段都是靜態常量,不能修改,所以註解裏定義的都是方法,而動態代理類
就是爲了能在運行時,調用註解定義的方法,就能獲取我們定義在註解上的值。
到這裏,對註解的實現已經有了一個大概的認識,不過一些細節,並沒有深究,能力有限,待需要時,有機會和能力再去深究。