在上篇文章 Spring 註解編程之模式註解 中咱們講到 Spring 模式註解底層原理,依靠 AnnotationMetadata
接口判斷是否存在指定元註解。html
這篇文章咱們主要深刻 AnnotationMetadata
,瞭解其底層原理。java
Spring 版本爲 5.1.8-RELEASE
使用 IDEA 生成 AnnotationMetadata
類圖,以下:web
AnnotationMetadata
存在兩個實現類分別爲 StandardAnnotationMetadata
與 AnnotationMetadataReadingVisitor
。StandardAnnotationMetadata
主要使用 Java 反射原理獲取元數據,而 AnnotationMetadataReadingVisitor
使用 ASM 框架獲取元數據。spring
Java 反射原理你們通常比較熟悉,而 ASM 技術可能會比較陌生,下面主要篇幅介紹 AnnotationMetadataReadingVisitor
實現原理。shell
基於
AnnotationMetadata#getMetaAnnotationTypes
方法,查看二者實現區別。
ASM 是一個通用的 Java 字節碼操做和分析框架。它能夠用於修改現有類或直接以二進制形式動態生成類。 ASM 雖然提供與其餘 Java 字節碼框架如 Javassist
,CGLIB
相似的功能,可是其設計與實現小而快,且性能足夠高。編程
Spring 直接將 ASM 框架核心源碼內嵌於 Spring-core
中,目前 Spring 5.1 使用 ASM 7 版本。架構
Java 源代碼通過編譯器編譯以後生成了 .class
文件。框架
Class文件是有8個字節爲基礎的字節流構成的,這些字節流之間都嚴格按照規定的順序排列,而且字節之間不存在任何空隙,對於超過8個字節的數據,將按 照Big-Endian的順序存儲的,也就是說高位字節存儲在低的地址上面,而低位字節存儲到高地址上面,其實這也是class文件要跨平臺的關鍵,由於 PowerPC架構的處理採用Big-Endian的存儲順序,而x86系列的處理器則採用Little-Endian的存儲順序,所以爲了Class文 件在各中處理器架構下保持統一的存儲順序,虛擬機規範必須對起進行統一。
Class 文件中包含類的全部信息,如接口,字段屬性,方法,在內部這些信息按照必定規則緊湊排序。ASM 框會以文件流的形式讀取 class 文件,而後解析過程當中使用觀察者模式(Visitor),當解析器碰到相應的信息委託給觀察者(Visitor)。工具
使用 ASM 框架首先須要繼承 ClassVisitor
,完成解析相應信息,如解析方法,字段等。post
而後使用 ClassReader
讀取類文件,而後再使用 ClassReader#accpet
接受 ClassVisitor
。
輸出結果爲:
com/spring/learning/customizescanning/asm/Person extends java/lang/Object { Lcom/spring/learning/customizescanning/asm/ASMAnnotation; Ljava/lang/String; name class org.objectweb.asm.Type I age class org.objectweb.asm.Type <init>()V add(II)I getName()Ljava/lang/String; setName(Ljava/lang/String;)V getAge()I setAge(I)V }
能夠看到 ClassVisitor
相應方法能夠用來解析類的相關信息,這裏咱們主要關注解析類上註解信息。解析註解將會在 ClassVisitor#visitAnnotation
完成解析。 該方法返回了一個 AnnotationVisitor
對象,其也是一個 Visitor 對象。後續解析器會繼續調用 AnnotationVisitor
內部方法進行再次解析。
以上實現採用 ASM Core API ,而 ASM 框架還提供 Tree API 用法。具體用法參考: https://asm.ow2.io/
AnnotationMetadataReadingVisitor#getMetaAnnotationTypes
方法實現很是簡單,直接從 metaAnnotationMap
根據註解類名稱獲取其上面全部元註解。註解相關信息解析由 AnnotationMetadataReadingVisitor#visitAnnotation
完成。
在 visitAnnotation
方法中,metaAnnotationMap
當作構造參數傳入了 AnnotationAttributesReadingVisitor
對象中,metaAnnotationMap
會在這裏面完成賦值。
AnnotationAttributesReadingVisitor#visitEnd
將會排除 java.lang.annotation
下的註解,而後經過遞歸調用 recursivelyCollectMetaAnnotations
獲取元註解,不斷將元註解置入 metaAnnotationMap
中。
最後使用 UML 時序圖中,歸納以上調用流程。
Spring 4 以後版本纔有遞歸查找元註解的方法。各位同窗能夠翻閱 Spring3 的版本做爲比較,能夠看出 Spring 的代碼功能也是逐漸迭代升級的。
StandardAnnotationMetadata
主要使用 Java 反射原理獲取相關信息。在 Spring 中封裝不少了反射工具類用於操做。
StandardAnnotationMetadata#getMetaAnnotationTypes
經過使用 Spring 工具類 AnnotatedElementUtils.getMetaAnnotationTypes
方法獲取。源碼調用比較清晰,各位同窗能夠自行翻閱理解,能夠參考下面時序圖理解,這裏再也不敘述。
本文介紹了 AnnotationMetadata
兩種實現方案,一種基於 Java 反射,另外一種基於 ASM 框架。
兩種實現方案適用於不一樣場景。StandardAnnotationMetadata
基於 Java 反射,須要加載類文件。而 AnnotationMetadataReadingVisitor
基於 ASM 框架無需提早加載類,因此適用於 Spring 應用掃描指定範圍內模式註解時使用。
另外歡迎加入 Java 極客技術知識星球,獲取最新 Java 技術。