先來一段簡單的代碼
//獲取資源
ClassPathResource resource = new ClassPathResource("spring-core.xml");
//獲取 BeanFactory
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
//根據新建的 BeanFactory 創建一個 BeanDefinitionReader 對象,該 Reader 對象爲資源的解析器
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
//裝載資源
reader.loadBeanDefinitions(resource);
整個過程分爲四個步驟:資源定位、裝載、解析、註冊
資源定位部分比較簡單,瞭解Resource、ResourceLoader接口以及常用實現類即可
裝載時序圖
從reader.loadBeanDefinitions(resource)開始
1、調用org.springframework.beans.factory.xml#XmlBeanDefinitionReader(資源解析器)的loadBeanDefinitions方法
2、將resource包裝成org.springframework.core.io.support.EncodedResource對象,主要是爲了對 Resource 進行編碼,保證內容讀取的正確性
5、通過encodedResource.getResource().getInputStream()獲取InputStream,再通過new InputSource(inputStream)包裝成org.xml.sax.InputSource對象,方便後續解析xml
8、通過getEntityResolver()獲取org.xml.sax.EntityResolver接口的實現類,該實現類是爲了尋找xml的驗證文件,也就是.dtd 、.xsd文件,Spring對EntityResolver接口的實現類是
org.springframework.beans.factory.xml.BeansDtdResolver
org.springframework.beans.factory.xml.PluggableSchemaResolver
9、獲取驗證模式,主要是爲了保證xml的正確性,以下是根據xml的內容獲取的結果,也可以手動指定
// 禁用驗證模式
public static final int VALIDATION_NONE = XmlValidationModeDetector.VALIDATION_NONE;
// 自動獲取驗證模式
public static final int VALIDATION_AUTO = XmlValidationModeDetector.VALIDATION_AUTO;
// DTD 驗證模式
public static final int VALIDATION_DTD = XmlValidationModeDetector.VALIDATION_DTD;
// XSD 驗證模式
public static final int VALIDATION_XSD = XmlValidationModeDetector.VALIDATION_XSD;
10、通過org.springframework.beans.factory.xml.DefaultDocumentLoader類最終調用jdk的 javax.xml 庫進行解析返回org.w3c.dom.Document接口實現類
至此裝載部分完成,接下來會對document進行解析,生成 BeanDefinition
解析document時序圖
接上一個時序圖,我們先看一下源碼執行的位置
//org.springframework.beans.factory.xml.XmlBeanDefinitionReader
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
//獲取Document實例
Document doc = doLoadDocument(inputSource, resource);
//註冊document
int count = registerBeanDefinitions(doc, resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
}
//...省略無關代碼
}
上一個時序圖執行到獲取Document實例,接下來通過registerBeanDefinitions方法完成document解析,生成BeanDefinition並且註冊
從上述圖中可以看出,整個過程主要用到了4個類(實際解析過程遠比上圖複雜)
XmlBeanDefinitionReader:
xml資源解析器,主要負責從xml文件中解析出document
XmlReaderContext:
解析過程中的上下文,包含了解析過程中的用到的配置資源、回調事件等
DefaultBeanDefinitionDocumentReader:
document解析類,包含了上下文XmlReaderContextBean和DefinitionParserDelegate,並且最後完成BeanDefinition註冊
DefinitionParserDelegate:
解析document生成BeanDefinition
1、通過反射創建DefaultBeanDefinitionDocumentReader
4、創建上下文XmlReaderContext
8、創建DefinitionParserDelegate
11、初始化DefinitionParserDelegate,解析xml節點,獲取lazyInit、merge、autowire、autowireCandidates、initMethod、destroyMethod等值
12、判斷節點的命名空間是否是默認命名空間(“http://www.springframework.org/schema/beans”),如果是,則調用parseDefaultElement解析
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);
}
}
14、如果不是默認命名空間,例如<tx:annotation-driven />,則調用parseCustomElement
最終會尋找META-INF/spring.handlers文件,根據命令空間可以找到相應的處理器進行解析,這裏就是通過TxNamespaceHandler對<tx:annotation-driven />進行解析
因爲篇幅原因,沒有具體展開解析的細節,建議大家主要細看第12步中對bean節點的解析,在這個過程中會生成BeanDefinition,並且完成註冊
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 進行 bean 元素解析。
// <1> 如果解析成功,則返回 BeanDefinitionHolder 對象。而 BeanDefinitionHolder 爲 name 和 alias 的 BeanDefinition 對象
// 如果解析失敗,則返回 null 。
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
// <2> 進行自定義標籤處理
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// <3> 進行 BeanDefinition 的註冊
// Register the final decorated instance.
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
//<4> 發出響應事件,通知相關的監聽器,已完成該 Bean 標籤的解析。
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
到這裏爲止解析和註冊完結,但是這個時候得到的是BeanDefinition對象,裏面只是裝載了生成bean所需的參數和配置,並不是真正的bean
未完待續…接下來會進入bean初始化流程