最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

16-Spring高級(jí)容器初始化:初步添加擴(kuò)展功能點(diǎn)

2023-06-17 14:28 作者:儒猿課堂  | 我要投稿

開(kāi)篇

上一節(jié),我們了解了ApplicationContext在初始化環(huán)節(jié)中對(duì)環(huán)境變量的一些初始化工作,并且,我們已經(jīng)發(fā)現(xiàn)創(chuàng)建了Spring初級(jí)容器BeanFactory,并且和之前一樣的是,ApplicationContext中的初級(jí)容器初始化也是委托給XmlBeanDefinitionReader完成的。


至此,ApplicationContext中的初級(jí)容器BeanFactory初始化工作就已經(jīng)完成了,這一節(jié),我們?cè)賮?lái)看下在ApplicationContext的初始化過(guò)程中,是如何對(duì)初級(jí)容器BeanFactory做一些功能拓展的,主要包括以下幾個(gè)部分:

1.先來(lái)看下什么是SPEL語(yǔ)言,并且Spring是如何支持SPEL語(yǔ)言的

2.然后看下Spring注冊(cè)的屬性編輯器是干什么的,具體有什么用

3.接著來(lái)看下ApplicationContext中又添加哪些感知接口

4.最后來(lái)看下Spring是如何對(duì)一些接口指定它們的依賴(lài)的


添加SPEL語(yǔ)言的支持


我們繼續(xù)回到主流程refresh方法中:

上一節(jié)我們看到了方法obtainFreshBeanFactory,發(fā)現(xiàn)是在初始化Spring的初級(jí)容器BeanFactory,方便后續(xù)為其擴(kuò)展各種各樣的功能。

接下來(lái),我們緊接著再到方法prepareBeanFactory里面看下:

可以看到,在方法prepareBeanFactory中也是做一些準(zhǔn)備的工作,當(dāng)然,相比于我們之前看到的prepareRefresh方法準(zhǔn)備一些環(huán)境變量信息而言,這里更多是對(duì)容器beanFactory做的一些功能擴(kuò)展,我們依次來(lái)看下。


首先,我們可以看到prepareBeanFactory方法為剛剛創(chuàng)建好的beanFactory,設(shè)置當(dāng)前上下文當(dāng)中的類(lèi)加載器,然后beanFactory調(diào)用方法setBeanExpressionResolver設(shè)置了一個(gè)StandardBeanExpressionResolver:

其中,StandardBeanExpressionResolver其實(shí)就是表達(dá)式解析器,也就是讓beanFactory支持表達(dá)式語(yǔ)言,這是ApplicationContext為beanFactory做的第一個(gè)功能擴(kuò)展點(diǎn)。

那什么是表達(dá)式語(yǔ)言呢?具體又讓beanFactory支持哪種表達(dá)式語(yǔ)言呢?我們可以到StandardBeanExpressionResolver構(gòu)造方法中簡(jiǎn)單來(lái)看下:

可以看到,在StandardBeanExpressionResolver的構(gòu)造方法中初始化了一個(gè)對(duì)象SpelExpressionParser,翻譯一下就是SPEL的表達(dá)式解析器,SPEL全稱(chēng)為Spring Expression Language也就是我們剛說(shuō)的Spring表達(dá)式語(yǔ)言。


那表達(dá)式語(yǔ)言長(zhǎng)什么樣子呢?我們?cè)赟tandardBeanExpressionResolver的類(lèi)中也可以看到一些端倪:

可以看到,默認(rèn)的表達(dá)式前綴為“#{”,后綴為“}”,拼接起來(lái)不就是“#{}”嗎,瞬間想起了以前在xml中經(jīng)常配置的一個(gè)場(chǎng)景:

可以看到,通過(guò)表達(dá)式“#{}”配置數(shù)據(jù)源的屬性算是早期使用Spring開(kāi)發(fā)時(shí),比較經(jīng)典的一個(gè)場(chǎng)景了,為了避免數(shù)據(jù)源配置信息耦合在xml文件中,我們可以單獨(dú)寫(xiě)一個(gè)配置文件db.properties,然后通過(guò)表達(dá)式“#{}”配置在xml中相應(yīng)的value屬性中。


那Spring會(huì)在什么時(shí)候使用StandardBeanExpressionResolver來(lái)解析xml中的“#{}”這樣的SPEL表達(dá)式呢?很簡(jiǎn)單,也就是在我們利用注冊(cè)好的BeanDefinition創(chuàng)建實(shí)例bean的時(shí)候。

在Spring創(chuàng)建實(shí)例bean時(shí),如果發(fā)現(xiàn)bean的某個(gè)屬性值存在表達(dá)式“#{}”,就會(huì)利用StandardBeanExpressionResolver來(lái)解析,后續(xù)我們分析到bean的實(shí)例化時(shí)就可以看到了。


不管怎么樣,我們目前畢竟還處于Spring容器的初始化階段,在這個(gè)階段看到的所有功能拓展,包括之前beanFactory解析xml文件、注冊(cè)BeanDefinition到Spring容器中,其實(shí)都是在初始化Spring容器。

這些初始化工作都是在為后續(xù)創(chuàng)建實(shí)例bean提供“原料”和擴(kuò)展功能的支持,所以,大家從現(xiàn)在開(kāi)始,需要留意一些功能擴(kuò)展點(diǎn)的代碼,方便后續(xù)bean加載環(huán)節(jié)的源碼理解。


添加屬性編輯器的注冊(cè)器ResourceEditorRegistrar


接下來(lái),我們?cè)賮?lái)下一個(gè)方法:

可以看到,接下來(lái)又為容器beanFactory設(shè)置了對(duì)象ResourceEditorRegistrar,翻譯一下大概是編輯資源的注冊(cè)器,那到底什么是注冊(cè)器呢?我們可以到ResourceEditorRegistrar中看一下:

簡(jiǎn)單瀏覽了之后,我們可以很容易的知道方法registerCustomEditors是ResourceEditorRegistrar類(lèi)中最關(guān)鍵的一個(gè)方法。

可以看到,方法registerCustomEditors的參數(shù)的類(lèi)型為PropertyEditorRegistry,翻譯下就是屬性編輯器的注冊(cè)器,也就是用來(lái)注冊(cè)各種屬性編輯器PropertyEditor的。


那為什么需要注冊(cè)屬性編輯器呢?我們繼續(xù)來(lái)看下:

可以看到在registerCustomEditors方法中多次調(diào)用了doRegisterEditor方法來(lái)注冊(cè)一些編輯器Editor,比如InputStream類(lèi)型的InputStreamEditor、File類(lèi)型的FileEditor、URL類(lèi)型的URLEditor等。

我們?cè)俚椒椒╠oRegisterEditor中看下:

可以看到,最后會(huì)將各種屬性類(lèi)型及對(duì)應(yīng)的屬性編輯器都注冊(cè)到registry中。

那Spring為什么要注冊(cè)這些屬性編輯器呢?我們還得要到這些屬性編輯器中尋找答案,看下它們里面到底在干些什么事情,比如,我們可以選擇到InputStreamEditor中看下:

看到這里大家應(yīng)該就明白了吧,首先會(huì)通過(guò)InputStreamEditor中的setAsText方法傳進(jìn)來(lái)一個(gè)String類(lèi)型的參數(shù)text,然后通過(guò)各種解析得到了InputStream。

也就是說(shuō),InputStreamEditor的功能是將字符串解析并轉(zhuǎn)換為InputStream的一個(gè)編輯器,同理,我們可以很容易的推測(cè)出FileEditor、URLEditor等其他編輯器Editor是用來(lái)將String解析轉(zhuǎn)換為相應(yīng)的對(duì)象的。


那為什么我們需要這些屬性編輯器,來(lái)將字符串String轉(zhuǎn)換為各種對(duì)象呢?其實(shí)這和xml配置文件的局限性有關(guān)。

比如一個(gè)bean中有一個(gè)屬性is,類(lèi)型為InputStream也就是一個(gè)輸入流,如果你想要在bean初始化時(shí)就給InputStream類(lèi)型的屬性is設(shè)置值,就很難在xml中配置InputStream,畢竟我們都知道在xml中只能配置字符串類(lèi)型的屬性值。

這個(gè)時(shí)候,InputStreamEditor就可以像我們看到的一樣通過(guò)字符串去解析資源,從而獲取到對(duì)應(yīng)的InputStream,然后再設(shè)置到bean相應(yīng)的屬性上,其他各種類(lèi)型的屬性也都有相應(yīng)屬性編輯器。

像我們剛才看到的一樣,Spring會(huì)在容器初始化環(huán)節(jié)就會(huì)注冊(cè)各種各樣的屬性編輯器,當(dāng)bean在實(shí)例化需要設(shè)置相應(yīng)的屬性值時(shí),這些屬性編輯器就會(huì)根據(jù)需要將相應(yīng)字符串String解析并轉(zhuǎn)換為相應(yīng)對(duì)象了,并為bean的這些屬性賦值,完成bean的實(shí)例化。


添加ApplicationContextAwareProcessor


了解完Spring中的屬性編輯器后,我們繼續(xù)往后面看:

可以看到,接下來(lái)會(huì)初始化一個(gè)ApplicationContextAwareProcessor類(lèi)型的對(duì)象,并添加到了beanFactory中。

我們隱約可以感覺(jué)到ApplicationContextAwareProcessor和我們之前講過(guò)的感知接口Aware有點(diǎn)關(guān)系,那ApplicationContextAwareProcessor具體是干什么的呢?我們進(jìn)去看下:

可以看到,ApplicationContextAwareProcessor一進(jìn)來(lái)就發(fā)現(xiàn)了一個(gè)非常顯眼的方法postProcessBeforeInitialization,很明顯它是ApplicationContextAwareProcessor類(lèi)中最關(guān)鍵的方法。

可以看到方法中,如果傳進(jìn)來(lái)對(duì)象bean不是EnvironmentAware、EmbeddedValueResolverAware、ResourceLoaderAware、ApplicationEventPublisherAware、MessageSourceAware、ApplicationContextAware中的任意一個(gè)接口實(shí)現(xiàn)類(lèi),直接就返回這個(gè)bean了。


很顯然,方法postProcessBeforeInitialization是專(zhuān)門(mén)處理這六個(gè)接口對(duì)應(yīng)的實(shí)現(xiàn)類(lèi)的,我們可以先來(lái)研究下這個(gè)六個(gè)接口:

通過(guò)類(lèi)繼承圖,我們可以看到這六個(gè)接口全都繼承Aware接口,也就是說(shuō)這六個(gè)接口都是感知接口。


之前講過(guò),如果一個(gè)bean實(shí)現(xiàn)了感知接口,Spring容器在實(shí)例化bean時(shí)就會(huì)調(diào)用感知接口的方法,將容器內(nèi)部的對(duì)象注入到感知接口方法中,這樣的話(huà)實(shí)現(xiàn)了感知接口的bean拿到了這些Spring內(nèi)部對(duì)象,就可以對(duì)Spring容器做自定義功能改造了。

比如說(shuō),我們經(jīng)常要在很多地方用到Spring的容器,通過(guò)容器獲取到一些bean,我們就可以讓bean預(yù)先實(shí)現(xiàn)ApplicationContextAware接口。

Spring在實(shí)例化bean的時(shí)候會(huì)通過(guò)調(diào)用方法setApplicationContext,將ApplicationContext注入到bean中了,bean獲取到ApplicationContext之后,容器中的所有bean就都可以獲取了。

我們回過(guò)頭來(lái)再看下,那postProcessBeforeInitialization方法中的核心邏輯是什么呢?我們繼續(xù)看下:

拋開(kāi)一些瑣碎的代碼,我們可以看到方法invokeAwareInterfaces比較關(guān)鍵,進(jìn)去看下:

看到這里我們應(yīng)該就都明白了,其實(shí)就是在調(diào)用這些感知接口的方法,將容器內(nèi)部的一些對(duì)象設(shè)置進(jìn)去,前面的一些推測(cè)也都實(shí)錘了。

而且,如果大家留意的話(huà)可以發(fā)現(xiàn),ApplicationContextAwareProcessor其實(shí)是繼承BeanPostProcessor接口的,BeanPostProcessor接口其實(shí)是Spring的后處理器接口,它是Spring提供的一個(gè)非常出色的擴(kuò)展點(diǎn)。

我們剛看到的邏輯其實(shí)都位于方法postProcessBeforeInitialization中,方法postProcessBeforeInitialization是接口BeanPostProcessor中的前置處理方法,Spring在實(shí)例化bean之前會(huì)統(tǒng)一執(zhí)行所有后處理器的前置處理方法,大家暫時(shí)可能會(huì)聽(tīng)著云里霧里,沒(méi)關(guān)系,接下來(lái)幾講我們馬上就要來(lái)詳細(xì)講解這塊內(nèi)容了。


添加需要忽略的感知接口


我們繼續(xù)看下方法prepareBeanFactory后面的邏輯:

熟悉的一幕又出現(xiàn)了,ignoreDependencyInterface方法不知道大家還記得不,我們?cè)谧铋_(kāi)始講解XmlBeanFactory初始化的環(huán)節(jié)就已經(jīng)詳細(xì)分析了,大家如果忘了可以去前面復(fù)習(xí)下。

簡(jiǎn)單來(lái)說(shuō),如果一個(gè)bean實(shí)現(xiàn)了傳入ignoreDependencyInterface方法的這些感知接口,Spring是不允許外界注入任何的依賴(lài)到bean中的,只允許Spring容器內(nèi)部調(diào)用感知接口的方法來(lái)注入相應(yīng)的依賴(lài)。


添加接口指定的依賴(lài)


和忽略感知接口方法ignoreDependencyInterface相對(duì)應(yīng)的,beanFactory會(huì)調(diào)用方法registerResolvableDependency來(lái)指定一批接口一定要注入指定的對(duì)象:

可以看到,beanFactory調(diào)用方法registerResolvableDependency,指定了接口以及接口對(duì)應(yīng)的依賴(lài)。

如方法registerResolvableDependency指定了接口BeanFactory的實(shí)現(xiàn)類(lèi)為當(dāng)前的beanFactory,ResourceLoader、ApplicationEventPublisher、ApplicationContext接口的實(shí)現(xiàn)類(lèi),都為this也就是當(dāng)前的對(duì)象。


為什么要為這些接口指定依賴(lài)呢?目的也很簡(jiǎn)單,如果要從Spring容器中獲取一個(gè)bean,如果這個(gè)bean實(shí)現(xiàn)的接口是BeanFactory、ResourceLoader、ApplicationEventPublisher、ApplicationContext中的任意一個(gè),就會(huì)直接返回方法registerResolvableDependency中設(shè)置進(jìn)去的對(duì)象。


比如,你要從Spring容器中獲取一個(gè)bean,恰好bean實(shí)現(xiàn)了接口ApplicationContext,此時(shí)就算你自己寫(xiě)了一個(gè)實(shí)現(xiàn)ApplicationContext接口的類(lèi)注入到Spring容器中,Spring最終也會(huì)忽略掉你寫(xiě)的那個(gè)bean,而使用方法registerResolvableDependency中設(shè)置設(shè)置進(jìn)去的beanFactory給你。

這樣的話(huà),Spring就可以保證Spring中的一些關(guān)鍵的接口,它們實(shí)現(xiàn)類(lèi)只能是Spring內(nèi)部指定的一些對(duì)象了。


在方法prepareBeanFactory的最后,就是一些其他瑣碎的代碼了:

比如注冊(cè)監(jiān)聽(tīng)器探查相關(guān)的后處理器的ApplicationListenerDetector、注冊(cè)和AspectJ織入相關(guān)的后處理器LoadTimeWeaverAwareProcessor以及注冊(cè)和上下文環(huán)境相關(guān)的一些對(duì)象,這些我們簡(jiǎn)單了解下就行了。


總結(jié)


好了,今天的知識(shí)點(diǎn)我們就講到這里了,我們來(lái)總結(jié)一下吧。


一張圖來(lái)梳理下當(dāng)前的流程:

這一節(jié),我們初步了解了一下ApplicationContext對(duì)beanFactory擴(kuò)展的一些功能點(diǎn),比較重要的幾點(diǎn)包括對(duì)SPEL表達(dá)式語(yǔ)言的支持、添加解析特定對(duì)象屬性的屬性編輯器、注冊(cè)調(diào)用感知接口方法的后處理器、添加需要忽略的感知接口以及制定接口對(duì)應(yīng)的依賴(lài)。

我們剛看到的ApplicationContext對(duì)beanFactory提供的一些功能擴(kuò)展點(diǎn)雖然比較零散,但是在實(shí)例化bean的時(shí)候還是比較有用的。


接下來(lái),我們即將要分析到Spring的后處理器了,雖然我們前面已經(jīng)看到Spring為我們預(yù)留了很多的功能擴(kuò)展點(diǎn),但是遠(yuǎn)沒(méi)有后處理器來(lái)的直接,接下來(lái)我們好好來(lái)看下這塊內(nèi)容。


16-Spring高級(jí)容器初始化:初步添加擴(kuò)展功能點(diǎn)的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國(guó)家法律
曲麻莱县| 富顺县| 如东县| 中西区| 德保县| 承德县| 汾阳市| 青冈县| 汝州市| 本溪市| 巴林右旗| 宁晋县| 景谷| 武义县| 昌都县| 保定市| 佛教| 兴山县| 泰宁县| 腾冲县| 凌海市| 石嘴山市| 澎湖县| 红河县| 福海县| 渭南市| 彰化县| 山阴县| 策勒县| 沧州市| 澳门| 谢通门县| 成都市| 依兰县| 冕宁县| 大宁县| 宁夏| 合川市| 东乌珠穆沁旗| 监利县| 藁城市|