XmlBeanDefinitionReader-----Spring原始碼解析 配置檔案裝載與解析
阿新 • • 發佈:2019-02-13
以下內容有部分摘自網路
步驟A. 讀取 Resource 檔案形成 Document 模型
類圖: XmlBeanFactory -> XmlBeanDefinitionReader
Spring 使用 XmlBeanDefinitionReader 來讀取並解析 xml 檔案,XmlBeanDefinitionReader 是 BeanDefinitionReader 介面的實現。
BeanDefinitionReader 定義了 Spring 讀取 Bean 定義的一個介面,這個介面中有一些 loadBeanDefinitions 方法, 用於讀取 Bean 配置。
BeanDefinitionReader 介面有兩個具體的實現,其中之一就是從 Xml 檔案中讀取配置的 XmlBeanDefinitionReader,另一個則是從 Java Properties 檔案中讀取配置的PropertiesBeanDefinitionReader。
(注:開發人員也可以提供自己的 BeanDefinitionReader 實現,根據自己的需要來讀取 spring bean 定義的配置。)
步驟B. 解析 Document 得到 Bean 配置
類圖: XmlBeanDefinitionReader-> BeanDefinitionDocumentReader
BeanDefinitionDocumentReader 介面中只定義了一個方法 registerBeanDefinitions. 有一個預設實現 DefaultBeanDefinitionDocumentReader.
DefaultBeanDefinitionDocumentReader 主要完成兩件事情,解析 <bean> 元素,為擴充套件 spring 的元素尋找合適的解析器,並把相應的元素交給解析器解析。
過程:
在 XmlBeanFactory 中建立了 XmlBeanDefinitionReader 的例項,並在 XmlBeanFactory 的構造方法中呼叫了 XmlBeanDefinitionReader 的 loadBeanDefinitions 方法,由 loadBeanDefinitions 方法負責載入 bean 配置並把 bean 配置註冊到 XmlBeanFactory 中。
在 XmlBeanDefinitionReader 的 loadBeanDefinitions 方法中, 呼叫 DefaultDocumentLoader 的 loadDocument 讀取配置檔案為 Document, 然後呼叫 BeanDefinitionDocumentReader 的 registerBeanDefinitions 方法 來解析 Bean.
原始碼解析:
在XmlBeanFactory初始化時, 需要指定Resource物件.
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory)
throws BeansException
{
super(parentBeanFactory);
reader = new XmlBeanDefinitionReader(this);
reader.loadBeanDefinitions(resource);
}
1. 先來分析下XmlBeanDefinitionReader這個類.
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader
接著
Java程式碼
public abstract class AbstractBeanDefinitionReader
implements BeanDefinitionReader
再繼續
Java程式碼
public interface BeanDefinitionReader
在BeanDefinitionReader中定義有許多loadBeanDefinitions方法
Java程式碼
public abstract int loadBeanDefinitions(Resource resource)
throws BeanDefinitionStoreException;
public abstract int loadBeanDefinitions(Resource aresource[])
throws BeanDefinitionStoreException;
public abstract int loadBeanDefinitions(String s)
throws BeanDefinitionStoreException;
public abstract int loadBeanDefinitions(String as[])
throws BeanDefinitionStoreException;
來回頭看XmlBeanDefinitionReader對loadBeanDefinitions方法的實現
在loadBeanDefinitions方法中呼叫了doLoadBeanDefinitions方法, 跟蹤doLoadBeanDefinitions方法
Java程式碼
Document doc = documentLoader.loadDocument(inputSource, getEntityResolver(), errorHandler, validationMode, isNamespaceAware());
通過一個叫documentLoader的東西的loadDocument方法來載入配置檔案形成DOM, 來看看documentLoader
Java程式碼
private DocumentLoader documentLoader
...
documentLoader = new DefaultDocumentLoader();
跟蹤到DefaultDocumentLoader
Java程式碼
public class DefaultDocumentLoader
implements DocumentLoader
...
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware)
throws Exception
{
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);
}
...
哦哦, 我們知道了, 是通過sax解析得到Dom的, 至於怎麼解析, 不屬於Spring範疇, 不做研究.在這一步, 已完成了從配置檔案讀取到Domcument. 接著要開始解析Dom了
再繼續, 解析成Dom後接著呼叫了registerBeanDefinitions方法
Java程式碼
return registerBeanDefinitions(doc, resource);
來看看registerBeanDefinitions的實現
Java程式碼
...
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
...
在這裡, 有一個BeanDefinitionDocumentReader介面, 實際上Spring對它有一個預設的實現類叫DefaultBeanDefinitionDocumentReader, 來看看它的家族
Java程式碼
public class DefaultBeanDefinitionDocumentReader
Java程式碼 public interface BeanDefinitionDocumentReader
BeanDefinitionDocumentReader只有一個registerBeanDefinitions方法
Java程式碼
public abstract void registerBeanDefinitions(Document document, XmlReaderContext xmlreadercontext)
throws BeanDefinitionStoreException;
該方法需要兩個引數, 一個是Document模型,這個應該是我們讀取配置檔案獲取到的, 另一個是XmlReaderContext物件, 我們在上面方法中看到是通過createReaderContext(resource)得到的, 那就看看具體如何得到
Java程式碼
protected XmlReaderContext createReaderContext(Resource resource)
{
if(namespaceHandlerResolver == null)
namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
return new XmlReaderContext(resource, problemReporter, eventListener, sourceExtractor, this, namespaceHandlerResolver);
}
能過建構函式new出來的, 且有一個重要引數resource
再繼續來看DefaultBeanDefinitionDocumentReader對BeanDefinitionDocumentReader的registerBeanDefinitions方法實現
Java程式碼
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext)
{
...
BeanDefinitionParserDelegate delegate = createHelper(readerContext, root);
...
parseBeanDefinitions(root, delegate);
...
}
嘿嘿, 開始解析Dom了哦, 其中主要是parseBeanDefinitions方法, 來看看具體是如何解析的
Java程式碼
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)
{
if(delegate.isDefaultNamespace(root.getNamespaceURI()))
{
NodeList nl = root.getChildNodes();
for(int i = 0; i < nl.getLength(); i++)
{
org.w3c.dom.Node node = nl.item(i);
if(node instanceof Element)
{
Element ele = (Element)node;
String namespaceUri = ele.getNamespaceURI();
if(delegate.isDefaultNamespace(namespaceUri))
parseDefaultElement(ele, delegate);
else
delegate.parseCustomElement(ele);
}
}
} else
{
delegate.parseCustomElement(root);
}
}
看到了吧, 迴圈解析Domcument節點
parseDefaultElement方法和delegate的parseCustomElement方法
先來看parseDefaultElement方法
Java程式碼
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate)
{
if(DomUtils.nodeNameEquals(ele, "import"))
importBeanDefinitionResource(ele);
else
if(DomUtils.nodeNameEquals(ele, "alias"))
processAliasRegistration(ele);
else
if(DomUtils.nodeNameEquals(ele, "bean"))
processBeanDefinition(ele, delegate);
}
看到這就很清楚了, 就是根據節點的名稱作不同解析, 如我們Spring配置檔案中常有以下幾種配置
Java程式碼
<import resource="classpath:xxx" />
<bean id="xxx" class="xxx.xxx.xxx" />
<alias name="xxxx" alias="yyyyy"/>
對<import>節點, 呼叫importBeanDefinitionResource方法解析, 此方法中, 又回到第一步讀取配置檔案並解析. 如此遞迴迴圈.
Java程式碼
...
Resource relativeResource = getReaderContext().getResource().createRelative(location);
int importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
...
對<alias>節點, 呼叫processAliasRegistration進行別名解析
我們主要看對<bean>節點呼叫processBeanDefinition進行解析
Java程式碼
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate)
{
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if(bdHolder != null)
{
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try
{
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch(BeanDefinitionStoreException ex)
{
getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex);
}
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
嘿嘿, 又用到delegate物件了, 且呼叫它的BeanDefinitionHolder方法, 返回一個BeanDefinitionHolder, 進去看它的parseBeanDefinitionElement方法Java程式碼
public class BeanDefinitionParserDelegate
{
private final Set usedNames = new HashSet();
...
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean)
{
... 解析id, name等屬性, 並驗證name是否唯一, 並將name儲存在usedNames中
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
...
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
...
}
可以看到, 在BeanDefinitionHolder中儲存了BeanDefinition的定義OK, 重頭戲開始, 最經典的部分出現了, 請看parseBeanDefinitionElement方法
Java程式碼
public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, BeanDefinition containingBean)
{
...
程式碼太長, 請參考具體程式碼
AbstractBeanDefinition bd = BeanDefinitionReaderUtils.createBeanDefinition(parent, className, readerContext.getBeanClassLoader());
...
return abstractbeandefinition;
...
}
在這個方法中, 解析了bean的所有屬性, 有最常用的class, scope, lazy-init等等. 並返回一個AbstractBeanDefinition例項. 至於具體怎麼解析, 就只能進一步跟蹤了, 不過既然到了這一步, 已經明白了它的基本原理, 具體實現就不作介紹這一步將節點解析成BeanDefinitionHolder物件, 再看看如何註冊, 回到DefaultBeanDefinitionDocumentReader的processBeanDefinition方法
看到對解析到的bdHolder物件又做了decorateBeanDefinitionIfRequired操作, 來看看實現
... 暫留空
接著呼叫了BeanDefinitionReaderUtils的registerBeanDefinition方法註冊bdHolder, 來看看如何實現的
Java程式碼
public class BeanDefinitionReaderUtils
{
public static void registerBeanDefinition(BeanDefinitionHolder bdHolder, BeanDefinitionRegistry beanFactory)
throws BeanDefinitionStoreException
{
String beanName = bdHolder.getBeanName();
beanFactory.registerBeanDefinition(beanName, bdHolder.getBeanDefinition());
String aliases[] = bdHolder.getAliases();
if(aliases != null)
{
for(int i = 0; i < aliases.length; i++)
beanFactory.registerAlias(beanName, aliases[i]);
}
}
}
看吧, 又呼叫了BeanDefinitionRegistry的registerBeanDefinition方法, 跟蹤之 (這個要看DefaultListableBeanFactory的實現)
Java程式碼
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry
{
private final Map beanDefinitionMap;
private final List beanDefinitionNames;
...
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException
{
...
Object oldBeanDefinition = beanDefinitionMap.get(beanName);
...
beanDefinitionMap.put(beanName, beanDefinition);
...
}
}
這裡, 看到了一個最最重要的物件就是beanDefinitionMap, 這個map存放了所有的bean物件, 和我們通常講的容器概念最為接近, getBean時實際是也是從這裡轔取物件, 相同的還有一個beanDefinitionNames, 但這個只儲存bean的名稱
完成上面之後, 還有一步操作beanFactory.registerAlias(beanName, aliases[i]);
這個實現實際是上AbstractBeanFactory抽象類所定義的
是不是特興奮, 已經揭開它神祕的面紗了
定義 -> 定位 -> 裝載 -> 註冊 這幾步已經完成了, 以後繼續看Spring是如何建立bean及例項化的