Java元注解meta-annotation和依賴(lài)注入詳解
這篇文章既介紹一個(gè)技術(shù),又記錄一個(gè)逐漸探索發(fā)現(xiàn)的過(guò)程,以供大家參考。
緣起
注意到Java的依賴(lài)注入DI規(guī)范(起初以為是CDI規(guī)范,然后發(fā)現(xiàn)是DI規(guī)范)有個(gè)叫@Qualifier的注解,用于當(dāng)一個(gè)interface或base class有多個(gè)實(shí)現(xiàn)類(lèi)時(shí),能選擇其中一個(gè)實(shí)現(xiàn)。如不用這一注解,一般的(按類(lèi)型)注入就會(huì)報(bào)錯(cuò)說(shuō)“不知道要在多個(gè)實(shí)現(xiàn)中選哪一個(gè)”。這一注解可以放在一個(gè)自定義注解上(例如@MyPreferredImplementation),從而將自定義注解變成一個(gè)qualifier annotation(限定符注解),然后只要在某一個(gè)實(shí)現(xiàn)類(lèi)上放上這個(gè)自定義注解,也在注入處放上這個(gè)自定義注解,就能起到連通雙方的作用,指定注入這個(gè)實(shí)現(xiàn)類(lèi)了,很方便也很語(yǔ)義化。(大家可以搜索學(xué)習(xí)@Qualifier的教程。)
Spring支持DI規(guī)范,而它自己也有一個(gè)叫@Qualifier注解(包名不相同,在spring的package里),不但支持以上功能,還可以直接放在待注入的變量上,用name參數(shù)(例如@Qualifier(name = “myBeanName”))來(lái)指定要注入的那個(gè)實(shí)現(xiàn)類(lèi)的bean name。Spring的這個(gè)功能好像更常用,至少在某公司就是這樣,DI規(guī)范的qualifier功能反而有些不為人所知了。
我認(rèn)為DI規(guī)范的更好,更加語(yǔ)義化。而這種把一個(gè)注解放在另一個(gè)注解上,是什么Java特性呢?起初不知道正確的關(guān)鍵詞,用“annotation on annotation”之類(lèi)的詞語(yǔ)左查右查也查不到。然后看JDK的Javadoc,看哪一個(gè)呢,看已知的幾個(gè)“annotation on annotation”,懂的朋友可能想到了,@Retention @Target @Inherited這些JDK內(nèi)置的用來(lái)放在另一個(gè)注解上的注解,Javadoc說(shuō)它們叫做元注解meta-annotation。JDK的這幾個(gè)元注解有很多文章講解,我就不講了,這一篇專(zhuān)講元注解。
探索
我就好奇了,依賴(lài)注入框架所用的元注解是怎么實(shí)現(xiàn)的?大家有想過(guò)嗎?比如說(shuō),框架怎么知道哪些注解被標(biāo)了@Qualifier元注解?第一反應(yīng)是Java內(nèi)置了這方面的支持,因?yàn)閱卧獪y(cè)試框架的@Test等注解也有元注解功能,這么常用的功能或許是Java原生支持的?
因此我就做了試驗(yàn),寫(xiě)兩個(gè)自定義注解,一個(gè)叫@Virtual元注解,一個(gè)叫@Real注解,把@Virtual放在@Real上,把@Real放到一個(gè)User類(lèi)上,看看編譯結(jié)果,然后用反射從這個(gè)類(lèi)上取@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類(lèi)上只標(biāo)有@Real注解,可證明編譯器沒(méi)有為元注解做什么工作。然后反射的結(jié)果也是不能從User類(lèi)拿到@Virtual,可證明JVM runtime也沒(méi)有為元注解做什么工作。因此@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也可以類(lèi)似地處理,對(duì)于掃描到的class,如果它具有@Qualifer注解,并且自身也是注解(實(shí)現(xiàn)了java.lang.Annotation interface),就作為一個(gè)自定義注解注冊(cè)到框架里(比如說(shuō),QualifierAnnotationRegistry?),如此一來(lái)框架就認(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文件,再在頁(yè)面中高亮關(guān)鍵詞”main”以過(guò)濾掉單元測(cè)試,憑經(jīng)驗(yàn)翻閱,在前3頁(yè)就能找到實(shí)現(xiàn)代碼了:
QualifierAnnotationAutowireCandidateResolver https://github.com/spring-pro... 用于注冊(cè)那些包含javax.inject.Qualifer的自定義注解。
CustomAutowireConfigurer https://github.com/spring-pro... 順便發(fā)現(xiàn)這個(gè)類(lèi)允許用戶(hù)手動(dòng)注冊(cè)自定義注解,無(wú)需元注解。
到此這篇關(guān)于Java元注解meta-annotation和依賴(lài)注入的文章就介紹到這了,更多相關(guān)Java元注解meta-annotation和依賴(lài)注入內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java利用自定義注解實(shí)現(xiàn)數(shù)據(jù)校驗(yàn)
JSR303是一套JavaBean參數(shù)校驗(yàn)的標(biāo)準(zhǔn),它提供了一系列的校驗(yàn)方式,這些校驗(yàn)方式在javax.validation.constraints包中。本文就來(lái)聊聊如何利用它實(shí)現(xiàn)數(shù)據(jù)校驗(yàn)2022-09-09
Springboot?+redis+谷歌開(kāi)源Kaptcha實(shí)現(xiàn)圖片驗(yàn)證碼功能
這篇文章主要介紹了Springboot?+redis+?歌開(kāi)源Kaptcha實(shí)現(xiàn)圖片驗(yàn)證碼功能,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-01-01
Java編程中隨機(jī)數(shù)的生成方式總結(jié)
在Java中利用自帶的類(lèi)庫(kù)可以有三種途徑可以產(chǎn)生隨機(jī)數(shù),這里我們舉了一些簡(jiǎn)單的例子來(lái)進(jìn)行Java編程中隨機(jī)數(shù)的生成方式總結(jié),需要的朋友可以參考下2016-05-05
生成8位隨機(jī)不重復(fù)的數(shù)字編號(hào)的方法
生成隨機(jī)不重復(fù)的數(shù)字編號(hào)在某些情況下也會(huì)用到,本文以生成8位隨機(jī)不重復(fù)的數(shù)字編號(hào)為例與大家分享下具體的實(shí)現(xiàn)過(guò)程,感興趣的朋友可以參考下2013-09-09
Java?@Scheduled定時(shí)任務(wù)不執(zhí)行解決辦法
這篇文章主要給大家介紹了關(guān)于Java?@Scheduled定時(shí)任務(wù)不執(zhí)行解決的相關(guān)資料,當(dāng)@Scheduled定時(shí)任務(wù)不執(zhí)行時(shí)可以根據(jù)以下步驟進(jìn)行排查和解決,需要的朋友可以參考下2023-10-10
Java實(shí)現(xiàn)經(jīng)典角色扮演偵探游戲游戲的示例代碼
這篇文章主要介紹了如何利用Java語(yǔ)言自制一個(gè)偵探文字游戲—《角色扮演偵探》,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編學(xué)習(xí)一下2022-02-02
SpringMVC解析post請(qǐng)求參數(shù)詳解
今天小編就為大家分享一篇解決SpringMVC接收不到ajaxPOST參數(shù)的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-08-08
idea創(chuàng)建JAVA Class時(shí)自動(dòng)生成頭部文檔注釋的方法
這篇文章主要介紹了idea創(chuàng)建JAVA Class時(shí)自動(dòng)生成頭部文檔注釋的方法,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12
淺談Java實(shí)現(xiàn)面向?qū)ο缶幊蘪ava oop
這篇文章主要介紹了淺談Java實(shí)現(xiàn)面向?qū)ο缶幊蘪ava oop,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07
springmvc+ajax+formdata上傳圖片代碼實(shí)例
這篇文章主要介紹了springmvc+ajax+formdata上傳圖片代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-09-09

