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

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

13-Spring初級容器初始化:BeanDefinition是如何注冊到Spring容器

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

開篇


上一節(jié),我們分析了bean標(biāo)簽下的各種子標(biāo)簽的解析,包括meta標(biāo)簽、lookup-method標(biāo)簽、replaced-method標(biāo)簽、constructor-arg標(biāo)簽、property標(biāo)簽等。

同時,我們也看到了對于constructor-arg標(biāo)簽和property標(biāo)簽而言,會進一步解析各種屬性的子標(biāo)簽,如set標(biāo)簽、list標(biāo)簽、map標(biāo)簽等,不管怎么樣,最終都是將這些標(biāo)簽解析到的信息封裝到BeanDefinition中。


但是,現(xiàn)在還有最后一些問題,那就是Spring容器是個什么東西呢?BeanDefinition又是如何注冊到Spring容器中的呢?這一節(jié)我們一起來看下,主要包含以下幾個部分:

1.xml標(biāo)簽解析到的BeanDefinition,是在哪里注冊到Spring容器中的

2.然后來看下BeanDefinition注冊到Spring容器前,會做哪些準(zhǔn)備性的工作

3.再看下Spring容器到底是什么東西,并且看下BeanDefinition是如何注冊到Spring容器中的

4.最后來看下BeanDefinition注冊到Spring容器后,如何注冊別名以及其完成其他的一些收尾工作


BeanDefinition注冊的入口


我們回到上一節(jié)源碼分析的位置:

可以看到,當(dāng)解析完各種標(biāo)簽后直接就將BeanDefinition返回了。

我們再往前,退回到調(diào)用方法parseBeanDefinitionElement的位置看下:

可以看到,前面解析bean標(biāo)簽下的各種屬性和標(biāo)簽都是在方法parseBeanDefinitionElement中完成了,然后得到GenericBeanDefinition類型的beanDefinition。

最后,我們可以看到parseBeanDefinitionElement方法其實就是將beanDefinition,連帶著解析得到的別名aliasesArray一起封裝到了BeanDefinitionHolder中,BeanDefinitionHolder我們可以理解為是持有BeanDefinition的一個對象而已。


接下來,我們再往前回退到上一個方法的位置看下:

可以看到,方法parseBeanDefinitionElement返回了我們剛才封裝好的BeanDefinitionHolder。

方法decorateBeanDefinitionIfRequired其實是對標(biāo)簽進行進一步的檢測,如果發(fā)現(xiàn)bean標(biāo)簽中還存在自定義標(biāo)簽,就對自定義標(biāo)簽進行解析,當(dāng)然自定義標(biāo)簽的解析我們這里不再深究,接下來,我們重點來看下BeanDefinitionReaderUtils中的registerBeanDefinition方法里面的邏輯,也就是BeanDefinition是如何注冊到Spring容器中的。


BeanDefinition注冊前的檢查


我們跟進到方法registerBeanDefinition中看下:

可以看到在registerBeanDefinition方法中,首先會從BeanDefinitionHolder中取出beanName以及BeanDefinition,然后調(diào)用registerBeanDefinition方法進行注冊。


我們繼續(xù)到方法registerBeanDefinition中看下:

方法中的代碼量比較多,我們依次來看下,首先,beanDefinition肯定是AbstractBeanDefinition的實例,所以調(diào)用方法validate進行最后一次校驗。

我們到validate方法中看下是如何校驗的:

可以看到,在方法validate中不允許methodOverrides屬性和factoryMethodName屬性同時設(shè)置,這是為什么呢?


不知道大家還記得,在上一節(jié)在解析lookup-method標(biāo)簽和replaced-method標(biāo)簽時,會將這兩個標(biāo)簽需要覆蓋的方法名設(shè)置到MethodOverrides中,一旦MethodOverrides不為空,這就意味著Spring創(chuàng)建出來bean還要重新覆寫這些方法。

而factoryMethodName屬性也就是工廠方法的名稱,了解過工廠設(shè)計模式的同學(xué)應(yīng)該都知道,通過工廠方法也可以創(chuàng)建一個bean出來,但是這相比于Spring默認的創(chuàng)建方式而言,算是一種不允許外界覆蓋bean中方法的創(chuàng)建方式了。

也就是說要么你通過工廠方法創(chuàng)建bean,要么就按Spring普通的方式來創(chuàng)建bean,兩者選其一,當(dāng)然這些后面我們在bean的加載環(huán)節(jié)會詳細講解,暫時只需要知道這兩種創(chuàng)建bean的方式是不一樣的,我們這里只能存在其中一種。

接下來,方法prepareMethodOverrides就是對MethodOverrides的一些準(zhǔn)備工作了,我們可以簡單進去看下:

可以看到,在prepareMethodOverrides方法中如果存在MethodOverrides屬性的話,就會通過方法prepareMethodOverride依次預(yù)處理這些需要覆蓋的方法。


預(yù)處理的方式也比較簡單,就是在方法prepareMethodOverride中判斷一下,如果lookup-method標(biāo)簽或replaced-method標(biāo)簽中配置了bean中需要覆蓋的方法,就將MethodOverride中的overloaded屬性值設(shè)置為false。

為什么要這樣設(shè)置overloaded屬性值為false呢?當(dāng)然是為了提高性能,也就是告訴Spring MethodOverride中記錄的那些需要覆蓋的方法,在bean中是沒有重載方法的,這樣的話,Spring就不需要額外根據(jù)參數(shù)去檢查bean中是否存在其他重載方法了,避免了一定的性能損耗。


BeanDefinition注冊到Spring容器中


對BeanDefinition最后的一輪檢查,說白了就是看下是否有異常的配置,并且設(shè)置一些參數(shù)提高創(chuàng)建bean時的效率。

接下來,我們繼續(xù)回到方法registerBeanDefinition中:

可以看到,beanDefinitionMap根據(jù)beanName獲取BeanDefinition,我們看下beanDefinitionMap是什么:

可以看到,beanDefinitionMap就是一個ConcurrentHashMap類型的Map,我們之前或多或少都聽說過Spring容器就是一個Map,確實,beanDefinitionMap就是大名鼎鼎Spring容器,Spring容器從本質(zhì)上來說就是一個Map。


因為beanDefinitionMap是成員變量,難免會有并發(fā)安全問題,所以這里使用多線程安全的ConcurrentHashMap作為Spring的容器。

我們繼續(xù)往后看:

可以看到,在BeanDefinition第一次來注冊時,從beanDefinitionMap中肯定是獲取不到任何東西的。

而且,BeanDefinition對應(yīng)的bean也還沒來得及創(chuàng)建,可以看到,第一次直接將beanName及對應(yīng)的beanDefinition設(shè)置到beanDefinitionMap中,同時將beanName記錄到beanDefinitionNames中。


假設(shè)剛才注冊的bean名稱為beanName1,這個時候如果又有一個bean過來注冊,并且bean的名稱也為beanName1,那會發(fā)生什么事情呢?會不會覆蓋之前注冊的beanName1對應(yīng)的value值呢?

我們接著來看下代碼:

可以看到,首先在if分支上有一個方法isAllowBeanDefinitionOverriding在約束著,通過方法的名稱我們可以知道,它是用來判斷beanDefinitionMap中的元素是否可以被覆蓋的。

如果方法isAllowBeanDefinitionOverriding返回結(jié)果為true,也就是允許相同名稱BeanDefinition覆蓋Spring容器的Map,可以看到就會將當(dāng)前bean的名稱beanName1,以及相應(yīng)的BeanDefinition設(shè)置到beanDefinitionMap中了。


當(dāng)然,這一切取決于方法isAllowBeanDefinitionOverriding方法返回的結(jié)果是什么,我們進去看下:

可以看到方法isAllowBeanDefinitionOverriding的返回結(jié)果,取決于成員變量allowBeanDefinitionOverriding,我們再看下allowBeanDefinitionOverriding:

可以看到,成員變量allowBeanDefinitionOverriding的默認值為true。

也就是說在默認情況下,如果出現(xiàn)相同名稱的多個bean來注冊,在Spring容器對應(yīng)的beanDefinitionMap中是允許被覆蓋的,所以這也在暗示我們在配置bean的時候,盡量不要出現(xiàn)相同名稱的bean,否則會被覆蓋。


最后一點收尾的代碼,我們也來看下:

可以看到,如果發(fā)現(xiàn)當(dāng)前不是這個名稱beanName第一次來注冊,或者當(dāng)前beanName還沒有創(chuàng)建相應(yīng)的單例時,將會調(diào)用方法resetBeanDefinition重置和beanName相關(guān)的一系列緩存。

當(dāng)然,這些都是一些很細節(jié)代碼了,比如,這里都已經(jīng)涉及到單例對象等和bean的加載相關(guān)的一些特性了,后面我們在分析bean的加載時,會看到各種各樣和bean單例創(chuàng)建相關(guān)的一系列緩存。


BeanDefinition別名alias的注冊

最后,我們再回到上一個方法中看下:

可以看到,現(xiàn)在就剩最后一個環(huán)節(jié)了也就是注冊bean的別名,也就是從BeanDefinitionHolder中獲取之前解析到的別名,然后依次遍歷注冊它們。


我們到方法registerAlias中看下:

可以看到,如果需要注冊的別名alias,和bean的名稱name相同,這就沒必要注冊了,并且會從aliasMap中刪除該別名。

其中,aliasMap的key為bean的別名,value為bean在Spring容器中的實際名稱,也就是我們之前看到的beanName。

如果alias和name不相同,此時會先從aliasMap中,根據(jù)alias獲取registeredName,如果registeredName和name相同,這就意味著名稱為name的別名已經(jīng)被其他的bean注冊了,就不需要重復(fù)注冊了。

我們繼續(xù)往下看:

如果registeredName和name不相同,而且因為根據(jù)別名alias從aliasMap成功獲取了一個非空的bean的名稱registeredName了,這就意味著別名alias已經(jīng)注冊了一個bean名稱為registeredName了。

此時,如果方法allowAliasOverriding返回false,也就是不允許別名被覆蓋,也就是說一個別名alias只能注冊一個bean的名稱不能注冊多個bean的名稱,事與愿違,這個時候直接就會拋異常了。

另外,我們可以看到還有一個方法checkForAliasCircle:

它主要是為了檢查是否存在別名循環(huán)的問題,比如,別名alias1對應(yīng)bean的名稱為name1,但是,如果同時還存在別名alias1對應(yīng)bean的名稱為name2,而name2又是bean name1的別名。

相當(dāng)于同時存在:alias1到name1,alias1到name2,name2再到name1這兩個關(guān)系,出現(xiàn)了兩個起點和重點都相同的循環(huán)了,這樣就造成了別名循環(huán)的問題就會拋異常。


現(xiàn)在,BeanDefinition注冊到Spring容器的核心環(huán)節(jié)已經(jīng)結(jié)束了,我們來看下:

最后,當(dāng)BeanDefinition注冊到Spring容器之后會調(diào)用fireComponentRegistered方法,發(fā)布一個BeanDefinition已經(jīng)被注冊的事件通知而已,當(dāng)然,一般我們都不需要監(jiān)聽這方面的事件。


總結(jié)


好了,今天的知識點,我們就講到這里了,我們來總結(jié)一下吧。

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

這一節(jié),我們找到BeanDefinition注冊的入口,發(fā)現(xiàn)了Spring容器其實就是一個Map,將bean注冊到Spring容器中的過程很簡單,就是以bean的名稱為key,以bean對應(yīng)的BeanDefinition為value注冊到beanDefinitionMap這個Map中。

另外,考慮到多線程安全問題,Spring容器對應(yīng)的Map也就是beanDefinitionMap,其實是多線程安全類型的ConcurrentHashMap。

到這里為止,初級的Spring容器也就是XmlBeanFactory的初始化環(huán)節(jié)已經(jīng)結(jié)束,XmlBeanFactory畢竟是初級的Spring容器,但是ApplicationContext這樣的高級容器相比于BeanFactory而言,擴展了哪些更高級別的功能呢?

接下來的章節(jié),我們好好來看下ApplicationContext的初始化是怎樣的。


13-Spring初級容器初始化:BeanDefinition是如何注冊到Spring容器的評論 (共 條)

分享到微博請遵守國家法律
三门县| 新巴尔虎右旗| 吐鲁番市| 大冶市| 朔州市| 崇明县| 芦溪县| 周宁县| 长汀县| 桃江县| 石河子市| 武威市| 遂昌县| 马鞍山市| 北京市| 永川市| 佛山市| 扶绥县| 房产| 临沭县| 闽侯县| 平邑县| 沅陵县| 察隅县| 云龙县| 岗巴县| 突泉县| 辽阳市| 五台县| 满城县| 米泉市| 永德县| 子长县| SHOW| 文安县| 鄯善县| 墨玉县| 青海省| 广灵县| 乌拉特后旗| 当阳市|