Spring IoC 容器初始化(2)
前情回顧
前文「Spring IoC 容器初始化」 以 IoC 容器中的 ClassPathXmlApplicationContext 為例進(jìn)行了深入分析。
Spring 從我們的配置文件(即 application-ioc.xml)中讀取到 Bean 的原始信息,將其解析為 Document 對象。由于 DOM 解析只是充當(dāng)了工具(語法解析),不必舍本逐末,這里不再深入分析。
本文繼續(xù)分析 Spring 如何從 Document 進(jìn)行語義解析和注冊 BeanDefinition。
BeanDefinition 解析和注冊
前文提到,Spring 從 Document 解析和注冊 BeanDefinition 是在 XmlBeanDefinitionReader#registerBeanDefinitions 方法中實現(xiàn)的,繼續(xù)跟進(jìn)這個方法:
public?class?XmlBeanDefinitionReader?extends?AbstractBeanDefinitionReader?{
????//?...
????public?int?registerBeanDefinitions(Document?doc,?Resource?resource)?throws?BeanDefinitionStoreException?{
??????//?創(chuàng)建?BeanDefinitionDocumentReader?對象
??????BeanDefinitionDocumentReader?documentReader?=?createBeanDefinitionDocumentReader();
??????int?countBefore?=?getRegistry().getBeanDefinitionCount();
??????//?將?Document?中的?Bean?信息注冊到容器
??????documentReader.registerBeanDefinitions(doc,?createReaderContext(resource));
??????return?getRegistry().getBeanDefinitionCount()?-?countBefore;
????}
????//?...
}
該方法主要做了兩件事:
創(chuàng)建 BeanDefinitionDocumentReader 對象 注冊 Document 中的 Bean 信息
這里又有個 BeanDefinitionDocumentReader,聯(lián)系前文的 BeanDefinitionReader、ResourceLoader 等,這就是面向?qū)ο缶幊蹋∣OP)思想的一種典型實現(xiàn),值得細(xì)品。
第二步實際調(diào)用了 DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions 方法:
public?class?DefaultBeanDefinitionDocumentReader?implements?BeanDefinitionDocumentReader?{
????//?...
????protected?void?doRegisterBeanDefinitions(Element?root)?{
??????BeanDefinitionParserDelegate?parent?=?this.delegate;
??????//?創(chuàng)建解析?BeanDefinition?的代理對象?BeanDefinitionParserDelegate
??????this.delegate?=?createDelegate(getReaderContext(),?root,?parent);
??????if?(this.delegate.isDefaultNamespace(root))?{
????????String?profileSpec?=?root.getAttribute(PROFILE_ATTRIBUTE);
????????if?(StringUtils.hasText(profileSpec))?{
??????????String[]?specifiedProfiles?=?StringUtils.tokenizeToStringArray(
??????profileSpec,?BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
??????????if?(!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles))?{
????????????if?(logger.isDebugEnabled())?{
??????logger.debug("Skipped?XML?bean?definition?file?due?to?specified?profiles?["?+?profileSpec?+
????????"]?not?matching:?"?+?getReaderContext().getResource());
????????????}
????????????return;
??????????}
????????}
??????}
??????//?解析?XML?配置文件前做的事情
??????preProcessXml(root);
??????//?解析?XML?配置文件
??????parseBeanDefinitions(root,?this.delegate);
??????//?解析?XML?配置文件后做的事情
??????postProcessXml(root);
??????this.delegate?=?parent;
???}
???//?...
}
該方法創(chuàng)建了一個 BeanDefinitionParserDelegate 對象,看它的名字可知,它是用來解析 BeanDefinition 的代理對象。
在 createDelegate 方法中,還對創(chuàng)建的 BeanDefinitionParserDelegate 進(jìn)行了初始化,主要是保存了 default-lazy-init、default-autowire、default-init-method 等。
真正對 XML 配置文件進(jìn)行語義解析的是 parseBeanDefinitions 方法。而且在該方法的前后,Spring 還留出了兩個方法 preProcessXml 和 postProcessXml。這兩個方法都是空的,用于自定義擴(kuò)展。
一個框架或軟件之所以做得好,除了本身確實好用,「擴(kuò)展性」也是很重要的一方面。
想起了 Tomcat 有很多參數(shù)可配置,JVM 的參數(shù)也有一大堆……
下面研究 parseBeanDefinitions 方法是如何解析 Bean 定義的。
public?class?DefaultBeanDefinitionDocumentReader?implements?BeanDefinitionDocumentReader?{
????//?...
????protected?void?parseBeanDefinitions(Element?root,?BeanDefinitionParserDelegate?delegate)?{
??????//?解析?Spring?默認(rèn)名稱空間
??????if?(delegate.isDefaultNamespace(root))?{
????????NodeList?nl?=?root.getChildNodes();
????????for?(int?i?=?0;?i???????????Node?node?=?nl.item(i);
??????????if?(node?instanceof?Element)?{
????????????Element?ele?=?(Element)?node;
????????????if?(delegate.isDefaultNamespace(ele))?{
??????????????parseDefaultElement(ele,?delegate);
????????????}
????????????else?{
??????????????delegate.parseCustomElement(ele);
????????????}
??????????}
????????}
??????}
??????//?解析自定義名稱空間
??????else?{
????????delegate.parseCustomElement(root);
??????}
???}
???//?...
}
Spring 默認(rèn)的名稱空間是什么呢?
還記得前面的配置文件 application-ioc.xml 嗎?如下:
<beans?xmlns="http://www.springframework.org/schema/beans"
???????xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
???????xsi:schemaLocation="http://www.springframework.org/schema/beans?http://www.springframework.org/schema/beans/spring-beans.xsd">
????
????<bean?id="person"?class="com.jaxer.doc.ioc.Person">
????????<property?name="pet"?ref="dog"/>
????bean>
????<bean?id="dog"?class="com.jaxer.doc.ioc.Dog">
????????<property?name="owner"?ref="person"/>
????????<property?name="age"?value="1"/>
????bean>
beans>
這里暫且跳過 Spring 如何解析自定義標(biāo)簽的 parseCustomElement 方法。先來分析 Spring 如何解析默認(rèn)名稱空間,即 parseDefaultElement 方法:
public?class?DefaultBeanDefinitionDocumentReader?implements?BeanDefinitionDocumentReader?{
????//?...
????//?解析?Spring?默認(rèn)名稱空間的配置
????private?void?parseDefaultElement(Element?ele,?BeanDefinitionParserDelegate?delegate)?{
??????//?解析?import?標(biāo)簽
??????if?(delegate.nodeNameEquals(ele,?IMPORT_ELEMENT))?{
????????importBeanDefinitionResource(ele);
??????}
??????//?解析?alias?標(biāo)簽
??????else?if?(delegate.nodeNameEquals(ele,?ALIAS_ELEMENT))?{
????????processAliasRegistration(ele);
??????}
??????//?解析?bean?標(biāo)簽
??????else?if?(delegate.nodeNameEquals(ele,?BEAN_ELEMENT))?{
????????processBeanDefinition(ele,?delegate);
??????}
??????//?解析?beans?標(biāo)簽
??????else?if?(delegate.nodeNameEquals(ele,?NESTED_BEANS_ELEMENT))?{
????????//?recurse
????????doRegisterBeanDefinitions(ele);
??????}
???}
???//?...
}
這里又分為四個部分,分別解析 import、alias、bean 和 beans 標(biāo)簽。
其實
標(biāo)簽是可以嵌套的,只是很少用到。若嵌套使用了 標(biāo)簽,會繼續(xù)遞歸去解析。
下面以
public?class?DefaultBeanDefinitionDocumentReader?implements?BeanDefinitionDocumentReader?{
????protected?void?processBeanDefinition(Element?ele,?BeanDefinitionParserDelegate?delegate)?{
??????//?通過?BeanDefinitionParserDelegate?解析出定義的?Bean?信息,并封裝為?BeanDefinitionHolder?對象
??????BeanDefinitionHolder?bdHolder?=?delegate.parseBeanDefinitionElement(ele);
??????if?(bdHolder?!=?null)?{
????????bdHolder?=?delegate.decorateBeanDefinitionIfRequired(ele,?bdHolder);
????????try?{
??????????//?將?BeanDefinition?注冊到注冊中心
??????????BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder,?getReaderContext().getRegistry());
????????}
????????//?catch
????????getReaderContext().fireComponentRegistered(new?BeanComponentDefinition(bdHolder));
??????}
????}
}
processBeanDefinition 方法主要做了兩件事:
對 Document 對象中的 Bean 定義進(jìn)行語義解析,并封裝為 BeanDefinitionHolder。BeanDefinitionHolder 持有了 BeanDefinition,并且保存了后者的別名等信息。 將 BeanDefinition 注冊到注冊中心,這里的注冊中心其實就是 IoC 容器,也就是 DefaultListableBeanFactory。
代碼走到這里,還沒看到 Spring 如何解析我們定義的
public?class?BeanDefinitionParserDelegate?{
??@Nullable
??public?BeanDefinitionHolder?parseBeanDefinitionElement(Element?ele)?{
????return?parseBeanDefinitionElement(ele,?null);
??}
??@Nullable
??public?BeanDefinitionHolder?parseBeanDefinitionElement(Element?ele,?@Nullable?BeanDefinition?containingBean)?{
????//?讀取?id、name?屬性
????String?id?=?ele.getAttribute(ID_ATTRIBUTE);
????String?nameAttr?=?ele.getAttribute(NAME_ATTRIBUTE);
????//?讀取別名?alias
????List?aliases?=?new?ArrayList<>();
????if?(StringUtils.hasLength(nameAttr))?{
??????String[]?nameArr?=?StringUtils.tokenizeToStringArray(nameAttr,?MULTI_VALUE_ATTRIBUTE_DELIMITERS);
??????aliases.addAll(Arrays.asList(nameArr));
????}
????String?beanName?=?id;
????if?(!StringUtils.hasText(beanName)?&&?!aliases.isEmpty())?{
??????beanName?=?aliases.remove(0);
????}
????if?(containingBean?==?null)?{
??????checkNameUniqueness(beanName,?aliases,?ele);
????}
????//?解析??標(biāo)簽定義的各個屬性
????AbstractBeanDefinition?beanDefinition?=?parseBeanDefinitionElement(ele,?beanName,?containingBean);
????if?(beanDefinition?!=?null)?{
??????if?(!StringUtils.hasText(beanName))?{
????????try?{
??????????if?(containingBean?!=?null)?{
????????????beanName?=?BeanDefinitionReaderUtils.generateBeanName(
????????beanDefinition,?this.readerContext.getRegistry(),?true);
??????????}
??????????else?{
????????????beanName?=?this.readerContext.generateBeanName(beanDefinition);
??????
????????????String?beanClassName?=?beanDefinition.getBeanClassName();
????????????if?(beanClassName?!=?null?&&
????????beanName.startsWith(beanClassName)?&&?beanName.length()?>?beanClassName.length()?&&
????????!this.readerContext.getRegistry().isBeanNameInUse(beanClassName))?{
??????????????aliases.add(beanClassName);
????????????}
????????? }
?????? }
?????? catch?(Exception?ex)?{
???????? error(ex.getMessage(),?ele);
???????? return?null;
?????? }
???? }
???? //?封裝為持有?BeanDefinition?對象的?BeanDefinitionHolder
???? String[]?aliasesArray?=?StringUtils.toStringArray(aliases);
???? return?new?BeanDefinitionHolder(beanDefinition,?beanName,?aliasesArray);
?? }
?? return?null;
??}
}
parseBeanDefinitionElement 方法主要做了三件事:
從 Document 讀取 標(biāo)簽中的 id 和 name 屬性 將 Document 中的 Bean 定義轉(zhuǎn)換為 BeanDefinition(實現(xiàn)類為 GenericBeanDefinition) 將 BeanDefinition 封裝為 BeanDefinitionHolder 并返回
拿到 BeanDefinitionHolder 后,Spring 會將其注冊到注冊中心。
到這里我們還是沒看到 Spring 是如何解析
留到后面再分析吧,本文先到這里,實在是太枯燥了??
小結(jié)
本文沿著上篇文章繼續(xù)跟進(jìn),好像沒什么實質(zhì)的東西……算了,就當(dāng)承前啟后吧。
欲知后事如何,且聽下回分解。

