javascript
Spring 注解编程之 AnnotationMetadata
在上篇文章 Spring 注解編程之模式注解 中我們講到 Spring 模式注解底層原理,依靠 AnnotationMetadata 接口判斷是否存在指定元注解。
這篇文章我們主要深入 AnnotationMetadata,了解其底層原理。
Spring 版本為 5.1.8-RELEASE
AnnotationMetadata 結(jié)構(gòu)
使用 IDEA 生成 AnnotationMetadata 類圖,如下:
AnnotationMetadata 存在兩個(gè)實(shí)現(xiàn)類分別為 StandardAnnotationMetadata與 AnnotationMetadataReadingVisitor。StandardAnnotationMetadata主要使用 Java 反射原理獲取元數(shù)據(jù),而 AnnotationMetadataReadingVisitor 使用 ASM 框架獲取元數(shù)據(jù)。
Java 反射原理大家一般比較熟悉,而 ASM 技術(shù)可能會(huì)比較陌生,下面主要篇幅介紹 AnnotationMetadataReadingVisitor 實(shí)現(xiàn)原理。
基于 AnnotationMetadata#getMetaAnnotationTypes方法,查看兩者實(shí)現(xiàn)區(qū)別。
AnnotationMetadataReadingVisitor
ASM 是一個(gè)通用的 Java 字節(jié)碼操作和分析框架。它可以用于修改現(xiàn)有類或直接以二進(jìn)制形式動(dòng)態(tài)生成類。 ASM 雖然提供與其他 Java 字節(jié)碼框架如 Javassist,CGLIB類似的功能,但是其設(shè)計(jì)與實(shí)現(xiàn)小而快,且性能足夠高。
Spring 直接將 ASM 框架核心源碼內(nèi)嵌于 Spring-core中,目前 Spring 5.1 使用 ASM 7 版本。
ASM 框架簡(jiǎn)單應(yīng)用
Java 源代碼經(jīng)過(guò)編譯器編譯之后生成了 .class 文件。
Class文件是有8個(gè)字節(jié)為基礎(chǔ)的字節(jié)流構(gòu)成的,這些字節(jié)流之間都嚴(yán)格按照規(guī)定的順序排列,并且字節(jié)之間不存在任何空隙,對(duì)于超過(guò)8個(gè)字節(jié)的數(shù)據(jù),將按 照Big-Endian的順序存儲(chǔ)的,也就是說(shuō)高位字節(jié)存儲(chǔ)在低的地址上面,而低位字節(jié)存儲(chǔ)到高地址上面,其實(shí)這也是class文件要跨平臺(tái)的關(guān)鍵,因?yàn)?PowerPC架構(gòu)的處理采用Big-Endian的存儲(chǔ)順序,而x86系列的處理器則采用Little-Endian的存儲(chǔ)順序,因此為了Class文 件在各中處理器架構(gòu)下保持統(tǒng)一的存儲(chǔ)順序,虛擬機(jī)規(guī)范必須對(duì)起進(jìn)行統(tǒng)一。
Class 文件中包含類的所有信息,如接口,字段屬性,方法,在內(nèi)部這些信息按照一定規(guī)則緊湊排序。ASM 框會(huì)以文件流的形式讀取 class 文件,然后解析過(guò)程中使用觀察者模式(Visitor),當(dāng)解析器碰到相應(yīng)的信息委托給觀察者(Visitor)。
使用 ASM 框架首先需要繼承 ClassVisitor,完成解析相應(yīng)信息,如解析方法,字段等。
然后使用 ClassReader 讀取類文件,然后再使用 ClassReader#accpet 接受 ClassVisitor。
輸出結(jié)果為:
com/spring/learning/customizescanning/asm/Person extends java/lang/Object {Lcom/spring/learning/customizescanning/asm/ASMAnnotation; Ljava/lang/String; name class org.objectweb.asm.TypeI age class org.objectweb.asm.Type<init>()Vadd(II)IgetName()Ljava/lang/String;setName(Ljava/lang/String;)VgetAge()IsetAge(I)V }可以看到 ClassVisitor 相應(yīng)方法可以用來(lái)解析類的相關(guān)信息,這里我們主要關(guān)注解析類上注解信息。解析注解將會(huì)在 ClassVisitor#visitAnnotation完成解析。 該方法返回了一個(gè) AnnotationVisitor 對(duì)象,其也是一個(gè) Visitor 對(duì)象。后續(xù)解析器會(huì)繼續(xù)調(diào)用 AnnotationVisitor內(nèi)部方法進(jìn)行再次解析。
以上實(shí)現(xiàn)采用 ASM Core API ,而 ASM 框架還提供 Tree API 用法。具體用法參考:https://asm.ow2.io/
AnnotationMetadataReadingVisitor#getMetaAnnotationTypes 源碼解析
AnnotationMetadataReadingVisitor#getMetaAnnotationTypes 方法實(shí)現(xiàn)非常簡(jiǎn)單,直接從 metaAnnotationMap 根據(jù)注解類名稱獲取其上面所有元注解。注解相關(guān)信息解析由 AnnotationMetadataReadingVisitor#visitAnnotation 完成。
在 visitAnnotation 方法中,metaAnnotationMap當(dāng)做構(gòu)造參數(shù)傳入了 AnnotationAttributesReadingVisitor 對(duì)象中,metaAnnotationMap會(huì)在這里面完成賦值。
AnnotationAttributesReadingVisitor#visitEnd 將會(huì)排除 java.lang.annotation 下的注解,然后通過(guò)遞歸調(diào)用 recursivelyCollectMetaAnnotations獲取元注解,不斷將元注解置入 metaAnnotationMap中。
最后使用 UML 時(shí)序圖中,概括以上調(diào)用流程。
Spring 4 之后版本才有遞歸查找元注解的方法。各位同學(xué)可以翻閱 Spring3 的版本作為比較,可以看出 Spring 的代碼功能也是逐漸迭代升級(jí)的。
StandardAnnotationMetadata
StandardAnnotationMetadata 主要使用 Java 反射原理獲取相關(guān)信息。在 Spring 中封裝很多了反射工具類用于操作。
StandardAnnotationMetadata#getMetaAnnotationTypes 通過(guò)使用 Spring 工具類 AnnotatedElementUtils.getMetaAnnotationTypes方法獲取。源碼調(diào)用比較清晰,各位同學(xué)可以自行翻閱理解,可以參考下面時(shí)序圖理解,這里不再敘述。
總結(jié)
本文介紹了 AnnotationMetadata兩種實(shí)現(xiàn)方案,一種基于 Java 反射,另一種基于 ASM 框架。
兩種實(shí)現(xiàn)方案適用于不同場(chǎng)景。StandardAnnotationMetadata基于 Java 反射,需要加載類文件。而 AnnotationMetadataReadingVisitor基于 ASM 框架無(wú)需提前加載類,所以適用于 Spring 應(yīng)用掃描指定范圍內(nèi)模式注解時(shí)使用。
擴(kuò)展閱讀
另外歡迎加入 Java 極客技術(shù)知識(shí)星球,獲取最新 Java 技術(shù)。
轉(zhuǎn)載于:https://www.cnblogs.com/goodAndyxublog/p/11218619.html
總結(jié)
以上是生活随笔為你收集整理的Spring 注解编程之 AnnotationMetadata的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: ASP.NET 2.0 中的资源与本地化
- 下一篇: SpringMVC学习笔记(二)常用注解