Spring xml默認標簽的解析

????????????????????默認標簽的解析

之前提高過Spring中的標簽包括默認標簽和自定義標簽兩種顽悼,而兩種標簽的用法以及解析方式存在著很大的不同仿荆,本章節(jié)重點帶領讀者詳細分析默認標簽的解析過程铅协。
默認標簽的解析是在parseDefaultElement函數中進行的篷角,函數中的功能邏輯一目了然锹引,分別對4種不同標簽(import盛卡、alias助隧、bean、和beans)做了不同的處理。

bean標簽的解析及注冊

在4種標簽的解析中并村,對bean標簽的解析最為復雜也是最重要巍实,所以我們從此標簽開始深入分析,如果能理解此標簽的解析過程哩牍,其他標簽的解析自然會迎刃而解棚潦。首先我們進入函數processBeanDefinition(ele, delegate)。

上述邏輯如下:
(1)首先委托BeanDefinitionDelegate類的parseBeanDefinitionElement方法進行元素解析膝昆,返回BeanDefinitionHolder類型的實例bdHolder丸边,經過這個方法后,bdHolder實例已經包含我們配置文件中配置的各種屬性了荚孵,例如class妹窖、name、id收叶、alias之類的屬性骄呼。
(2)當返回的bdHolder不為空的情況下若存在默認標簽的子節(jié)點下再有自定義屬性,還需要再次對自定義標簽進行解析判没。
(3)解析完成后蜓萄,需要對解析后的bdHolder進行注冊,同樣澄峰,注冊操作委托給了BeanDefinitionReaderUtils的registerBeanDefinition方法嫉沽。是一個一個bean進行注冊的。
(4)最后發(fā)出響應事件摊阀,通知相關的監(jiān)聽器耻蛇,這個Bean已經加載完成了。

解析BeanDefinition

下面我們就針對各個操作做具體分析胞此。首先我們從元素解析及信息提取開始,也就是BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele)跃捣,進入BeanDefinitionDelegate類的parseBeanDefinitionElement方法漱牵。

以上兩個代碼塊截圖是一個方法,是對默認標簽解析的全過程了疚漆。對xml解析如洋蔥般一層一層剝酣胀。整個解析過程我們很明了了。在開始對屬性展開全面解析錢娶聘,Spring在外層又做了一個當前層的功能架構闻镶,在當前層完成的主要工作包括如下內容。
(1)提取元素中的id以及name屬性丸升。
(2)進一步解析其他所有屬性并統(tǒng)一封裝至GenericBeanDefinition類型的實例中铆农。
(3)如果檢測到bean沒有指定beanName,那么使用默認規(guī)則為此bean生成beanName狡耻。
(4)將獲取到的信息封裝到BeanDefinitionHolder的實例中墩剖。
進一步查看(2)中對標簽其他屬性解析的過程猴凹。

終于,bean標簽的所有屬性岭皂,不論常用的還是不常用的我們都看到了郊霎,盡管有些復雜的屬性還需要進一步的解析,不過絲毫不會影響我們興奮的心情爷绘。接下來书劝,我們繼續(xù)一些復雜標簽屬性的解析。

創(chuàng)建用于屬性承載的BeanDefinition

BeanDefinition是一個接口土至,在Spring中存在三種實現(xiàn):RootBeanDefinition庄撮、ChildBeanDefinition以及GenericBeanDefinition。三種實現(xiàn)均繼承了AbstractBeanDefinition毙籽,其中BeanDefinition是配置文件<bean>元素標簽在容器中的內部表示形式洞斯。<bean>元素標簽擁有class、scope坑赡、lazy-init等配置屬性烙如,BeanDefinition則提供了相應的beanClass、scope毅否、lazyInit屬性亚铁,BeanDefinition和<bean>中的屬性是一一對應的。其中RootBeanDefinition是最常用的實現(xiàn)類螟加,它對應一般性的<bean>元素標簽徘溢,GenericBeanDefinition是自2.5版本以后新加入的bean文件配置屬性定義類,是一站式服務類捆探。
在配置文件中可以定義父<bean>和子<bean>然爆,父<bean>用RootBeanDefinition表示,而子<bean>用ChildBeanDefinition表示黍图,而沒有父<bean>的<bean>就使用RootBeanDefinition表示曾雕。AbstractBeanDefinition對兩者共同的類信息進行抽象。
Spring通過BeanDefinition將配置文件中的<bean>配置信息轉換為容器的內部表示助被,并將這些BeanDefinition注冊到BeanDefinitionRegistry中剖张。Spring容器的BeanDefinitionRegistry就像是Spring配置信息的內存數據庫,主要是以Map的形式保存揩环,后續(xù)操作直接從BeanDefinitionRegistry中讀取配置信息搔弄。查看BeanDefinition繼承圖:

由此可知,要解析屬性首先要創(chuàng)建用于承載屬性的實例丰滑,也就是創(chuàng)建GenericBeanDefinition類型的實例顾犹。而代碼createBeanDefinition(className, parent)的作用就是實現(xiàn)此功能。

解析各種屬性

當我們創(chuàng)建了bean信息的承載實例后,便可以進行bean信息的各種屬性解析了蹦渣,首先我們進入parseBeanDefinitionAttributes方法哄芜。parseBeanDefinitionAttributes方法是對element所有元素屬性進行解析:

我們可以清楚地看到Spring完成了對所有bean屬性的解析,這些屬性中有很多是我們經常使用的柬唯,同時我相信也一定會有或多或少的屬性是讀者不熟悉或者是沒有使用過的认臊。

解析子元素meta

在開始解析元數據的分析前,我們先回顧下元數據meta屬性的使用锄奢。
<bean id="myTestBean" class="bean.MyTestBean" >
? ? ? ? <meta key="testStr" value="aaaaaaa" />
</bean>
這段代碼并不會現(xiàn)在MyTestBean的屬性當中失晴,而是一個額外的聲明,當需要使用里面的信息的時候可以通過BeanDefinition的getAttribute(key)方法進行獲取拘央。
對meta屬性的解析代碼如下:

涂屁。。灰伟。拆又。。栏账。對屬性的解析源碼后續(xù)再添加



AbstractBeanDefinition屬性

至此我們便完成了對xml文檔到GenericBeanDefinition的轉換帖族,也就是說到這里,XML中所有的配置都可以在GenericBeanDefinition的實例類中找到對應的配置挡爵。
GenericBeanDefinition只是子類實現(xiàn)竖般,而大部分的通用屬性都保存在了AbstractBeanDefinition中,那么我們再次通過AbstractBeanDefinition的屬性來回顧一下我們都解析了那些對應的配置茶鹃。


解析默認標簽中的自定義標簽元素

完成了默認標簽的解析與提取過程涣雕,或許涉及的內容太多,我們已經忘了我們從哪個函數開始了闭翩,回到默認標簽解析函數的起始函數:

我們已經用了大量的篇幅分析了BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele)這句代碼挣郭,接下來,我們要進行 bdHolder = delegate.decorateBeanDefinitionRequired(ele, bdHolder)代碼的分析男杈,首先大致了解下這句代碼的作用丈屹,其實我們可以從語義上分析:如果需要的話就對beanDefinition進行裝飾,這句代碼到底是什么功能呢伶棒?代碼適用于這樣的場景:
<bean id = "test" class="test.MyClass" >
? ? <mybean :user username="aaa"/>
</bean>
當Spring中的bean使用的是默認的標簽配置,但是其中的子元素卻使用了自定義的配置時彩库,這句代碼便會起作用了肤无。可能有人會疑問骇钦,對bean的解析分為兩種類型宛渐,一種是默認類型的解析,另一種是自定義類型的解析,這不正是自定義類型的解析嗎窥翩?為什么會在默認類型解析中單獨添加一個方法處理呢业岁?確實,這個問題很讓人迷惑寇蚊,但是笔时,不知道你是否發(fā)現(xiàn),這個自定義類型并不是以Bean的形式出現(xiàn)的呢仗岸?我們之前講過的兩種類型的不同處理只是針對Bean的允耿,這里我們看到,這個自定義類型其實是屬性扒怖。繼續(xù)分析一下這段代碼:? ?
看如下代碼较锡,函數方法中第三個參數設置為空,第三個參數是父類bean盗痒,當對某個嵌套配置進行分析時蚂蕴,這里需要傳遞父類beanDefinition。分析源碼得知這里傳遞的參數其實是為了使用父類的scope屬性俯邓,以備子類若沒有設置scope時默認使用父類的屬性骡楼,這里分析的是頂層配置,所以傳遞null看成。將第三個參數設置為空進一步跟蹤函數:

上面的代碼君编,我們看到函數分別對元素的所有屬性以及子節(jié)點進行了decorateIfRequired函數的調用,我們繼續(xù)跟蹤代碼:

程序走到這里川慌,條理其實已經非常清楚了吃嘿,首先獲取屬性或者元素的命名空間,以此來判斷該元素或者屬性是否適用于自定義標簽的解析條件梦重,找出自定義類型所對應的NamespaceHandler并進行進一步解析兑燥。在自定義標簽解析的章節(jié)我們會重點講解,這里暫時先略過琴拧。
我們總結下decorateBeanDefinitionIfRequired方法的作用降瞳,在decorateBeanDefinitionIfRequired中我們可以看到對于程序默認的標簽的處理其實是直接略過的,因為默認的標簽到這里已經被處理完了蚓胸,這里只對自定義的標簽或者說對bean的自定義屬性感興趣挣饥。在方法中實現(xiàn)了尋找自定義標簽并根據自定義標簽尋找命名空間處理器,并進行進一步的解析沛膳。

注冊解析的BeanDefinition

對于配置文件扔枫,解析也解析完了,裝飾也裝飾完了锹安,對于得到的BeanDefinition已經可以滿足后續(xù)的使用需求了短荐,唯一還剩下的工作就是注冊了倚舀,也就是processBeanDefinition函數中的BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegister()代碼)

從上面的代碼可以看出,解析的beanDefinition都會被注冊到BeanDefinitionRegister類型的實例register中忍宋,而對于beanDefinition的注冊分成了兩部分:通過beanName的注冊以及通過別名的注冊痕貌。

1.通過beanName注冊BeanDefinition

對于BeanDefinition的注冊,或許很多人認為的方式就是將beanDefinition直接放入map中就好了糠排,使用beanName作為key舵稠。確實,Spring就是這么做的乳讥,只不過除此之外柱查,它還做了點別的事情。


上面的代碼中我們看到云石,在對于bean的注冊處理方式上唉工,主要進行了幾個步驟。
(1)對AbstractBeanDefinition的校驗汹忠。在解析XML文件的時候我們提過校驗淋硝,但是此校驗非彼校驗,之前的校驗是針對于XML格式的校驗宽菜,而此時的校驗是針對于與AbstractBeanDefinition的methodOverrides屬性的谣膳。
(2)對beanName已經注冊的情況的處理。如果設置了不允許bean的覆蓋铅乡,則需要拋出異常继谚,否則直接覆蓋。
(3)加入map緩存阵幸。
(4)清除解析之前留下的對應beanName的緩存花履。

2.通過別名注冊BeanDefinition

別名注冊相對于名稱注冊Bean更容易讀懂。

由以上代碼中可以得到注冊alias的步驟如下:
(1)alias與beanName相同情況處理挚赊。若alias與beanName并名稱相同則不需要處理并刪除掉原有alias诡壁。
(2)alias覆蓋處理。若aliasName已經使用并沒有指向了另一個beanName則需要用戶的設置進行處理荠割。
(3)alias循環(huán)檢查妹卿。當A->B存在時,若再次出現(xiàn)A->C->B時候則會拋出異常蔑鹦。
(4)注冊alias夺克。

通知監(jiān)聽器解析并注冊完成

通過代碼getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder))完成此工作,這里的實現(xiàn)只為擴展嚎朽,當程序開發(fā)人員需要對注冊BeanDefinition事件進行監(jiān)聽時可以通過注冊監(jiān)聽器的方式并將處理邏輯寫入監(jiān)聽器中懊直,目前Spring中并沒有對此事件做任何邏輯處理。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末火鼻,一起剝皮案震驚了整個濱河市室囊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌魁索,老刑警劉巖融撞,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異粗蔚,居然都是意外死亡尝偎,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門鹏控,熙熙樓的掌柜王于貴愁眉苦臉地迎上來致扯,“玉大人,你說我怎么就攤上這事当辐《督” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵缘揪,是天一觀的道長耍群。 經常有香客問我,道長找筝,這世上最難降的妖魔是什么蹈垢? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮袖裕,結果婚禮上曹抬,老公的妹妹穿的比我還像新娘。我一直安慰自己急鳄,他們只是感情好谤民,可當我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著攒岛,像睡著了一般赖临。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上灾锯,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天兢榨,我揣著相機與錄音,去河邊找鬼顺饮。 笑死吵聪,一個胖子當著我的面吹牛,可吹牛的內容都是我干的兼雄。 我是一名探鬼主播吟逝,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼赦肋!你這毒婦竟也來了块攒?” 一聲冷哼從身側響起励稳,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎囱井,沒想到半個月后驹尼,有當地人在樹林里發(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡庞呕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年新翎,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片住练。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡地啰,死狀恐怖,靈堂內的尸體忽然破棺而出讲逛,到底是詐尸還是另有隱情亏吝,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布妆绞,位于F島的核電站顺呕,受9級特大地震影響,放射性物質發(fā)生泄漏括饶。R本人自食惡果不足惜株茶,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望图焰。 院中可真熱鬧启盛,春花似錦、人聲如沸技羔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽藤滥。三九已至鳖粟,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間拙绊,已是汗流浹背向图。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留标沪,地道東北人榄攀。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像金句,于是被迫代替她去往敵國和親呐萌。 傳聞我的和親對象是個殘疾皇子仰剿,可洞房花燭夜當晚...
    茶點故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內容