Spring容器主要分爲兩類BeanFactory和ApplicationContext,後者是基於前者的功能擴展,也就是一個基礎容器和一個高級容器的區別。本篇就以BeanFactory基礎容器接口的默認實現類XmlBeanFactory啓動流程分析來入門Spring源碼的學習。node
對Spring有些瞭解的應該XmlBeanFactory已通過時了。沒錯,本篇要講的XmlBeanFactory在Spring3.1這個好久遠版本就開始過期了。git
@deprecated as of Spring 3.1 in favor of {@link DefaultListableBeanFactory}
取而代之的寫法以下github
ClassPathResource resource=new ClassPathResource("spring-config.xml");
DefaultListableBeanFactory beanFactory=new DefaultListableBeanFactory();
XmlBeanDefinitionReader beanDefinitionReader=new XmlBeanDefinitionReader(beanFactory);
beanDefinitionReader.loadBeanDefinitions(resource);
歸納DefaultListableBeanFactory + XmlBeanDefinitionReader 取代了 XmlBeanFactory容器的建立和初始化,能夠聯想到經過組合的方式靈活度是比XmlBeanFactory高的,針對不一樣的資源讀取組合的方式只需替換不一樣的讀取器BeanDefinitionReader就能夠了,而XmlBeanFactory中的讀取器XmlBeanDefinitionReader限定其只能讀取XML格式的文件資源,因此至於XmlBeanFactory被廢棄的緣由可想而知。web
<?xml version="1.0" encoding=" UTF-8" standalone="yes"?><root>
<code>0</code>
<message>調用成功</message>
</root>
附上DOM4J解析的代碼spring
String xml="<?xml version=\"1.0\" encoding=\" UTF-8\" standalone=\"yes\"?><root>\n" +
"<code>0</code>\n" +
"<message>調用成功</message>\n" +
"</root>";
Document document = DocumentHelper.parseText(xml);
Element root = document.getRootElement();
String code = root.elementText("code");
String message =root.elementText("message");
XML文檔被解析成DOM樹,其中Document是整個DOM的根節點,root爲根元素,由根元素一層一層向下解析element元素,容器啓動解析XML流程就是這樣。設計模式
XmlBeanFactory容器啓動就兩行代碼數組
ClassPathResource resource = new ClassPathResource("spring-config.xml");
XmlBeanFactory beanFactory = new XmlBeanFactory(resource);
怎麼樣?看似簡單就證實了Java封裝的強大,但背後藏了太多。 這裏就送上XmlBeanFactory啓動流程圖,對應的就是上面的兩行代碼。數據結構
這是啥?!看得頭暈的看官老爺們別急着給差評啊(瑟瑟發抖中)。這裏精簡下,想快速理解的話直接看精簡版後的,想深刻到細節建議看下上面的時序圖。 整個就是bean的加載階段。經過解析XML中的標籤元素生成beanDefinition註冊到beanDefinitionMap中。併發
按照XmlBeanFactory啓動流程的前後順序整理的關鍵性代碼索引列表,其中一級索引爲類,二級索引對應其類下的方法。符號 ---▷表示接口的實現。建議能夠觀察方法索引的參數變化(資源轉換)來分析整個流程,來加深對流程的理解。編輯器
XmlBeanFactory
XmlBeanDefinitionReader
DefaultBeanDefinitionDocumentReader ---▷ BeanDefinitionDocumentReader
BeanDefinitionParserDelegate
DefaultListableBeanFactory ---▷ BeanDefinitionRegistry
好了,當你內心對這個流程有個大概的樣子以後,這裏就能夠繼續走下去。建議仍是很模糊的同窗自重,避免走火入魔想對我人身攻擊的。下面就每一個方法重要的點一一解析,嚴格按照方法的執行前後順序來講明。
功能概述: XmlBeanFactory的構造方法,整個容器啓動的入口,完成bean工廠的實例化和BeanDefinition加載(解析和註冊)。
/**
* XmlBeanFactory
**/
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, null);
}
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
// 加載(解析註冊)BeanDefinition
this.reader.loadBeanDefinitions(resource);
}
知識點:
功能概述: 上面根據ClassPathResource資源訪問策略拿到了資源Resource,這裏將Resource進行特定的編碼處理,而後將編碼後的Resource轉換成SAX解析XML文件所須要的輸入源InputSource。
/**
* XmlBeanDefinitionReader
**/
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
// 把從XML文件讀取的Resource資源進行編碼處理
return loadBeanDefinitions(new EncodedResource(resource));
}
/**
* XmlBeanDefinitionReader
**/
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
...
// 完成resource->inputStream->inputSource轉換,使用SAX方式接收輸入源InputSource解析XML
try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
// 執行加載BeanDefinition
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
...
}
知識點:
功能概述: 獲取DOM Document對象,XmlBeanDefinitionReader自己沒有對文檔讀取的能力,而是委託給DocumentLoader的實現類DefaultDocumentLoader去讀取輸入源InputResource從而獲得Document對象。得到Document對象後,接下來就是BeanDefinition的解析和註冊。
/**
* XmlBeanDefinitionReader
**/
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
...
try {
// inputSource->DOM Document
Document doc = doLoadDocument(inputSource, resource);
int count = registerBeanDefinitions(doc, resource);
return count;
}
...
}
知識點:
功能概述: 解析DOM Document成容器的內部數據接口BeanDefinition並註冊到容器內部。
/**
* XmlBeanDefinitionReader
**/
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
// 已註冊的BeanDefinition的數量
int countBefore = getRegistry().getBeanDefinitionCount();
// DOM Document->BeanDefinition,註冊BeanDefinition至容器
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
// 返回這次註冊新增的BeanDefinition數量
return getRegistry().getBeanDefinitionCount() - countBefore;
}
知識點:
功能概述: 在此以前一直 是XML加載解析的準備階段,在獲取到Document對象以後就開始真正的解析了。
/**
* DefaultBeanDefinitionDocumentReader
**/
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
doRegisterBeanDefinitions(doc.getDocumentElement());
}
功能概述: 解析Element,DefaultBeanDefinitionDoucumentReader一樣不具備解析Element的能力,委託給BeanDefinitionParserDelegate執行。
/**
* DefaultBeanDefinitionDocumentReader
**/
protected void doRegisterBeanDefinitions(Element root) {
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
...
// 解析前處理,留給子類實現
preProcessXml(root);
parseBeanDefinitions(root, this.delegate);
// 解析後處理,留給子類實現
postProcessXml(root);
this.delegate = parent;
}
知識點:
功能概述: 判別XML中Bean的聲明標籤是默認的仍是自定義的,執行不一樣的解析邏輯。對於根節點或者子節點是默認命名空間採用parseDefaultElement,不然使用parseCustomElement對自定義命名空間解析。
/**
* DefaultBeanDefinitionDocumentReader
**/
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate){
// 對beans的處理
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)) {
// 對默認的Bean標籤解析
parseDefaultElement(ele, delegate);
}
else {
// 對自定義的Bean標籤解析
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
知識點:
Spring中XML有兩大類Bean的聲明標籤
默認聲明標籤:
<bean id="userService" class="com.fly4j.service.impl.UserServiceImpl"/>
自定義聲明標籤: ·
<tx: annotation-driven />
怎麼區分是默認命名空間仍是自定義命名空間?
經過node.getNamespaceURI()獲取命名空間並和Spring中固定的命名空間http://www.springframework.org/schema/beans進行比對,若是一致則默認,不然自定義。
功能概述: 上面說到Spring標籤包括默認標籤和自定義標籤兩種。解析這兩種方式分別不一樣。如下就默認標籤進行說明BeanDefinitionParseDelegate對Bean標籤元素的解析過程。
/**
* DefaultBeanDefinitionDocumentReader
**/
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
// import標籤
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
// alias標籤
processAliasRegistration(ele);
}
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
// bean標籤
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// beans標籤
doRegisterBeanDefinitions(ele);
}
}
知識點:
Spring的4種默認標籤舉例:
import
<import resource="spring-config.xml"/>
alias
<bean id="userService" class="com.fly4j.service.impl.UserServiceImpl"></bean>
<alias name="userService" alias="user" />
bean
<beans>
<bean id="userService" class="com.fly4j.service.impl.UserServiceImpl"></bean>
</beans>
beans
<beans>
<bean id="userService" class="com.fly4j.service.impl.UserServiceImpl"></bean>
</beans>
功能概述: 在4中默認標籤當中,其中核心的是對bean標籤的解析。如下就對bean標籤的解析來看解析過程。
/**
* DefaultBeanDefinitionDocumentReader
**/
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate){
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); // 參考4.1源碼
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); // 參考4.2源碼
try {
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); // 參考5.1源碼
}
...
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
流程解讀:
功能概述: 經過BeanDefinitionParseDelegate對Bean標籤的解析,解析獲得id和name這些信息封裝到BeanDefinitionHolder並返回。
/**
* BeanDefinitionParseDelegate
**/
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
}
/**
* BeanDefinitionParseDelegate
**/
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
// 解析id屬性
String id = ele.getAttribute(ID_ATTRIBUTE);
// 解析name屬性
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
List<String> 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);
}
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
...
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
}
功能概述: 4.1是對默認標籤的屬性解析,那若是標籤有自定義屬性和自定義子節點,這時就要經過decorateBeanDefinitionIfRequired解析這些自定義屬性和自定義子節點。
/**
* BeanDefinitionParseDelegate
**/
public BeanDefinitionHolder decorateBeanDefinitionIfRequired(
Element ele, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {
BeanDefinitionHolder finalDefinition = originalDef;
// Decorate based on custom attributes first.
// 首先對自定義屬性解析和裝飾
NamedNodeMap attributes = ele.getAttributes();
for (int i = 0; i < attributes.getLength(); i++) {
Node node = attributes.item(i);
finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
}
// Decorate based on custom nested elements.
// 裝飾標籤下的自定義子節點
NodeList children = ele.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node node = children.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
}
}
return finalDefinition;
}
功能概述: 經過beanName註冊BeanDefinition至BeanDefinitionMap中去,至此完成Bean標籤的解析,轉換成Bean在容器中的數據結構BeanDefinition.
/**
* BeanDefinitionReaderUtils
**/
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// Register bean definition under primary name.
// 使用beanName註冊BeanDefinition
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// Register aliases for bean name, if any.
// 使用別名註冊BeanDefiniion
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
/**
* BeanDefinitionParseDelegate
**/
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
...
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
...
this.beanDefinitionMap.put(beanName, beanDefinition);
}else{
if (hasBeanCreationStarted()) {
// beanDefinitionMap是全局變量,會存在併發訪問問題
synchronized (this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
removeManualSingletonName(beanName);
}
}
else {
// Still in startup registration phase
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
removeManualSingletonName(beanName);
}
this.frozenBeanDefinitionNames = null;
}
...
}
總結Spring IOC基礎容器XmlBeanFactory的啓動流程歸納以下:
本篇就XmlBeanFactory容器啓動流程分析和源碼解析兩個角度來對Spring IOC容器有個基礎的認識。有了這篇基礎,下篇就開始對Spring的擴展容器ApplicationContext進行分析。最後但願你們看完都有所收穫,能夠的話給個關注,感謝啦。
附上我編譯好的Spring源碼,版本是當前最新版本5.3.0,歡迎star