XML的解析機(jī)制
通過AbstractXmlApplicationContext類可以看到,XML解析是由XmlBeanDefinitionReader類實(shí)現(xiàn)的:
我們先看一看spring的xml配置文件的結(jié)構(gòu):
Spring的xml配置文件遵循了xml規(guī)范,我們常用的component-scan或者aop config分別在context和aop這兩個(gè)命名空間中脉让,beans是根標(biāo)簽呜叫,beans的每一個(gè)子標(biāo)簽對(duì)應(yīng)一個(gè)BeanDefinition對(duì)象苍日。
我們?cè)倏纯碨pring在xml解析的思路府喳,打開spring-context包或者spring-aop包脖卖,以context包為例帖鸦,可以在jar包的META-INF目錄下看到以下文件:
打開其中的spring.handlers文件,可以看到以下內(nèi)容:
文件內(nèi)容是命名空間與某個(gè)NamespaceHandler接口的實(shí)現(xiàn)類的映射關(guān)系胚嘲,aop包中的spring.handlers文件的內(nèi)容也類似:
再打開spring.schemas文件作儿,可以看到類似如下內(nèi)容:
其中三個(gè)方法:
1. init方法表示初始化;
2. parse方法表示解析一個(gè)BeanDefinition對(duì)象并注冊(cè)到BeanDefinitionRegistry接口中馋劈,BeanDefinitionRegistry接口和BeanDefinition的意義前文描述過攻锰,這里不再重述;
3. decorator方法表示解析一個(gè)xml標(biāo)簽妓雾,并對(duì)Bean定義做裝飾娶吞,所謂的裝飾即對(duì)bean定義的子標(biāo)簽做處理;
打開一個(gè)NamespaceHandler類械姻,比如AopNamespaceHandler妒蛇,可以看到其init方法中在注冊(cè)BeanDefinitionParser和BeanDefinitionDecorator對(duì)象:
對(duì)應(yīng)NamespaceHandler接口中的方法,registerBeanDefinitionParser方法則是注冊(cè)用于將該命名空間下的某個(gè)標(biāo)簽解析成BeanDefinition對(duì)象楷拳,registerBeanDefinitionDecoreator方法則是用于解析某個(gè)Bean配置的該命名空間下的子標(biāo)簽绣夺。打開某個(gè)BeanDefinitionParser的實(shí)現(xiàn),即可看到解析bean定義(BeanDefinition)欢揖。
回到XmlBeanDefinitionReader類陶耍,此類中除了讀取xml文件的內(nèi)容外,有一個(gè)初始化相關(guān)的重要屬性namespaceHandlerResolver她混,此屬性的接口類型如下:
接口聲明非常簡單烈钞,即通過namespace的url獲取NamespaceHandler,從此接口的定義來看正是使用spring.handlers文件中的內(nèi)容并為bean定義的解析提供服務(wù)坤按。從XmlBeanDefinitionReader接口中可以看到此屬性的初始化:
默認(rèn)使用的是DefaultNamespaceHandlerResolver類毯欣,此類的實(shí)現(xiàn)比較簡單,加載spring.handlers文件并初始化NamespaceHandler臭脓。
XML配置的擴(kuò)展
前文描述了spring的xml配置解析的思路酗钞,是通過在jar包的META-INF/spring.handlers文件中的NamespaceHandler來解析bean的,基于spring的xml解析機(jī)制,我們可以自定義實(shí)現(xiàn)xmlns以及相關(guān)的BeanDefinitionParser和BeanDefinitionDecorator算吩。
假設(shè)需要實(shí)現(xiàn)以下xml配置功能:
這個(gè)配置的意思是,/META-INF/app.properties文件中取變量(用${xxx}表示)佃扼,如果${env}的值等于local偎巢,則配置TestBean,如果存在${appName}則設(shè)置appName屬性兼耀,如果不存在${run_mode}則設(shè)置runMode屬性值為0压昼。
配置文件中新的標(biāo)簽有兩種使用方式:
1. 作為beans的第一級(jí)子標(biāo)簽,這種使用方式需要BeanDefinitionParser來處理
2. 作為bean標(biāo)簽的子標(biāo)簽瘤运,作用于property屬性之上窍霞,這種使用方式是對(duì)bean的裝飾過程,需要BeanDefinitionDecorator來處理
從這份配置內(nèi)容可以看出引入了新的名叫ctl的xmlns拯坟,ctl是自定義的命名空間但金,按照前文所述的spring xml解析機(jī)制,那么我們?yōu)閏tl命名空間編寫xsd文件以及spring.handlers和spring.schemas文件:
編寫CtlNamespaceHandler類:
以if標(biāo)簽為例郁季,IfBeanDefinitionParser類的實(shí)現(xiàn)如下(省略細(xì)節(jié)冷溃,后面有詳細(xì)代碼鏈接):
詳細(xì)代碼見github: https://github.com/gaohanghbut/springcfg