上一篇博客說(shuō)明了下spring是如何找到資源文件的觉壶,classpath下的xml,最終會(huì)被解析為 ClassPathContextResource
,下面進(jìn)一步分析虎忌,有了這個(gè)資源文件之后spring是如何將其解析為BeanDefinition的
入口XmlBeanDefinitionReader.loadBeanDefinitions
最開(kāi)始的入口归粉,只是包了下Resource
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
//EncodedResource只是存了Resource的編碼
return loadBeanDefinitions(new EncodedResource(resource));
}
1)保存當(dāng)前正在加載的資源
2)檢測(cè)是否有重復(fù)加載資源的情況
3)真正干活的地方doLoadBeanDefinitions
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
//... 省略日志
//1年堆、保存當(dāng)前正在加載的資源
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
//2、檢測(cè)是否有重復(fù)加載資源的情況
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绢掰、解析并注冊(cè)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
看下默認(rèn)實(shí)現(xiàn) DefaultDocumentLoader
@Override
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
//模板方法模式,子類繼承DefaultDocumentLoader后可以替換DocumentBuilderFactory和DocumentBuilder的實(shí)現(xiàn)
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 并 注冊(cè)BeanDefinition
有了document之后滴劲,就可以很方便的通過(guò)document的API來(lái)讀取xml的元素攻晒。
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
//beanDefininition的注冊(cè),委托給了BeanDefnitionDocumentReader
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
具體的注冊(cè)調(diào)用過(guò)程班挖,就不跟了鲁捏,總之,最后會(huì)調(diào)用到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)) {
//默認(rèn)標(biāo)簽的解析
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
//自定義標(biāo)簽的解析
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標(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);
}
}
到了這一步萧芙,spring開(kāi)始配置文件解析给梅,具體如何解析,以及如何將配置文件封裝為BeanDefinitions并進(jìn)行注冊(cè)双揪,下一篇博客會(huì)分析动羽。