Java元注解meta-annotation和依賴注入詳解
這篇文章既介紹一個(gè)技術(shù),又記錄一個(gè)逐漸探索發(fā)現(xiàn)的過程,以供大家參考。
緣起
注意到Java的依賴注入DI規(guī)范(起初以為是CDI規(guī)范,然后發(fā)現(xiàn)是DI規(guī)范)有個(gè)叫@Qualifier的注解,用于當(dāng)一個(gè)interface或base class有多個(gè)實(shí)現(xiàn)類時(shí),能選擇其中一個(gè)實(shí)現(xiàn)。如不用這一注解,一般的(按類型)注入就會(huì)報(bào)錯(cuò)說“不知道要在多個(gè)實(shí)現(xiàn)中選哪一個(gè)”。這一注解可以放在一個(gè)自定義注解上(例如@MyPreferredImplementation),從而將自定義注解變成一個(gè)qualifier annotation(限定符注解),然后只要在某一個(gè)實(shí)現(xiàn)類上放上這個(gè)自定義注解,也在注入處放上這個(gè)自定義注解,就能起到連通雙方的作用,指定注入這個(gè)實(shí)現(xiàn)類了,很方便也很語義化。(大家可以搜索學(xué)習(xí)@Qualifier的教程。)
Spring支持DI規(guī)范,而它自己也有一個(gè)叫@Qualifier注解(包名不相同,在spring的package里),不但支持以上功能,還可以直接放在待注入的變量上,用name參數(shù)(例如@Qualifier(name = “myBeanName”))來指定要注入的那個(gè)實(shí)現(xiàn)類的bean name。Spring的這個(gè)功能好像更常用,至少在某公司就是這樣,DI規(guī)范的qualifier功能反而有些不為人所知了。
我認(rèn)為DI規(guī)范的更好,更加語義化。而這種把一個(gè)注解放在另一個(gè)注解上,是什么Java特性呢?起初不知道正確的關(guān)鍵詞,用“annotation on annotation”之類的詞語左查右查也查不到。然后看JDK的Javadoc,看哪一個(gè)呢,看已知的幾個(gè)“annotation on annotation”,懂的朋友可能想到了,@Retention @Target @Inherited這些JDK內(nèi)置的用來放在另一個(gè)注解上的注解,Javadoc說它們叫做元注解meta-annotation。JDK的這幾個(gè)元注解有很多文章講解,我就不講了,這一篇專講元注解。
探索
我就好奇了,依賴注入框架所用的元注解是怎么實(shí)現(xiàn)的?大家有想過嗎?比如說,框架怎么知道哪些注解被標(biāo)了@Qualifier元注解?第一反應(yīng)是Java內(nèi)置了這方面的支持,因?yàn)閱卧獪y(cè)試框架的@Test等注解也有元注解功能,這么常用的功能或許是Java原生支持的?
因此我就做了試驗(yàn),寫兩個(gè)自定義注解,一個(gè)叫@Virtual元注解,一個(gè)叫@Real注解,把@Virtual放在@Real上,把@Real放到一個(gè)User類上,看看編譯結(jié)果,然后用反射從這個(gè)類上取@Virtual,看@Real能不能自動(dòng)引導(dǎo)到@Virtual上。示例代碼如下:
@Retention(RetentionPolicy.RUNTIME) public @interface Virtual { } @Virtual @Retention(RetentionPolicy.RUNTIME) public @interface Real { } @Real public class User { }
編譯后用IDE查看class文件,發(fā)現(xiàn)@Virtual元注解仍然只標(biāo)在@Real上,User類上只標(biāo)有@Real注解,可證明編譯器沒有為元注解做什么工作。然后反射的結(jié)果也是不能從User類拿到@Virtual,可證明JVM runtime也沒有為元注解做什么工作。因此@Qualifier的元注解特性極有可能是相關(guān)框架自行實(shí)現(xiàn)的。
要怎么實(shí)現(xiàn)呢?我們可以自己動(dòng)腦筋想一想??紤]到,Spring框架掃描所有的class文件(之所以要掃描class文件而非class對(duì)象,是因?yàn)镴ava不提供遍歷所有class對(duì)象的功能,使框架不得不重復(fù)實(shí)現(xiàn)對(duì)class文件的解析工作),將其中有相應(yīng)注解的class轉(zhuǎn)化為BeanDefinition注冊(cè)到BeanFactory。那么@Qualifier也可以類似地處理,對(duì)于掃描到的class,如果它具有@Qualifer注解,并且自身也是注解(實(shí)現(xiàn)了java.lang.Annotation interface),就作為一個(gè)自定義注解注冊(cè)到框架里(比如說,QualifierAnnotationRegistry?),如此一來框架就認(rèn)識(shí)所有的包含@Qualifier元注解的自定義注解了,之后要使用就順理成章了。
發(fā)現(xiàn)
那么Spring實(shí)際上是怎么實(shí)現(xiàn)的呢?我們可以查源碼。到GitHub上找到spring-framework這項(xiàng)目,搜索代碼關(guān)鍵詞Qualifer或javax.inject.Qualifier,查到90多個(gè)Java文件,再在頁面中高亮關(guān)鍵詞”main”以過濾掉單元測(cè)試,憑經(jīng)驗(yàn)翻閱,在前3頁就能找到實(shí)現(xiàn)代碼了:
QualifierAnnotationAutowireCandidateResolver https://github.com/spring-pro... 用于注冊(cè)那些包含javax.inject.Qualifer的自定義注解。
CustomAutowireConfigurer https://github.com/spring-pro... 順便發(fā)現(xiàn)這個(gè)類允許用戶手動(dòng)注冊(cè)自定義注解,無需元注解。
到此這篇關(guān)于Java元注解meta-annotation和依賴注入的文章就介紹到這了,更多相關(guān)Java元注解meta-annotation和依賴注入內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JAVA 時(shí)間區(qū)間的字符串合法性驗(yàn)證
需要對(duì)獲得的諸如08:30-11:00這樣的字符串進(jìn)行合法性驗(yàn)證,判定表示的時(shí)間區(qū)間是否合法,以及對(duì)高峰期時(shí)間的區(qū)間是否在總的時(shí)間區(qū)間內(nèi)部進(jìn)行判斷。2013-03-03Spring security如何重寫Filter實(shí)現(xiàn)json登錄
這篇文章主要介紹了Spring security 如何重寫Filter實(shí)現(xiàn)json登錄,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09Java實(shí)現(xiàn)商品管理系統(tǒng)代碼實(shí)例講解
這篇文章主要介紹了Java實(shí)現(xiàn)商品管理系統(tǒng)代碼實(shí)例講解,文中代碼實(shí)例講解的很清楚,有需要的同學(xué)可以借鑒參考下2021-02-02Disconf實(shí)現(xiàn)分布式配置管理的原理與設(shè)計(jì)
這篇文章主要為大家介紹了Disconf實(shí)現(xiàn)分布式配置管理的原理與設(shè)計(jì)分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2022-03-03springboot連接neo4j報(bào)錯(cuò)的解決方案
這篇文章主要介紹了springboot連接neo4j報(bào)錯(cuò)的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-02-02mybatisplus?@Select注解中拼寫動(dòng)態(tài)sql異常問題的解決
這篇文章主要介紹了mybatisplus?@Select注解中拼寫動(dòng)態(tài)sql異常問題的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12