上一篇部落格說明了下spring是如何找到資源檔案的,classpath下的xml,最終會被解析為 ClassPathContextResource
,下面進一步分析,有了這個資源檔案之後spring是如何将其解析為BeanDefinition的
入口XmlBeanDefinitionReader.loadBeanDefinitions
最開始的入口,隻是包了下Resource
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
//EncodedResource隻是存了Resource的編碼
return loadBeanDefinitions(new EncodedResource(resource));
}
1)儲存目前正在加載的資源
2)檢測是否有重複加載資源的情況
3)真正幹活的地方doLoadBeanDefinitions
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
//... 省略日志
//1、儲存目前正在加載的資源
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
//2、檢測是否有重複加載資源的情況
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
//document加載需要的的InputSource
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//3、真正幹活的地方doLoadBeanDefinitions
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
inputStream.close();
}
//... 省略 一系列異常處理
}
1、這裡做的兩件事,将資源檔案解析成Document
2、解析并注冊BeanDefinitions
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
Document doc = doLoadDocument(inputSource, resource);
return registerBeanDefinitions(doc, resource);
}
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
}
document的解析
XmlBeanDefinitionReader 将 Document的生成,委托給了DocumentLoader
看下預設實作 DefaultDocumentLoader
@Override
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
//模闆方法模式,子類繼承DefaultDocumentLoader後可以替換DocumentBuilderFactory和DocumentBuilder的實作
DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
if (logger.isDebugEnabled()) {
logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
}
DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
return builder.parse(inputSource);
}
這裡使用了工廠方法模式

解析xml 并 注冊BeanDefinition
有了document之後,就可以很友善的通過document的API來讀取xml的元素。
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
//beanDefininition的注冊,委托給了BeanDefnitionDocumentReader
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
具體的注冊調用過程,就不跟了,總之,最後會調用到org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseBeanDefinitions方法
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); 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);
}
}
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
//import标簽解析
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
//alias标簽解析
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
//bean标簽
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
//beans标簽
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
到了這一步,spring開始配置檔案解析,具體如何解析,以及如何将配置檔案封裝為BeanDefinitions并進行注冊,下一篇部落格會分析。