上回「spring ioc 容器初始化(2)」說到了 spring 如何解析我們定義的 標籤,**跟進了一層又一層,跋山涉水,最終來到了 beandefinitionparserdelegate#parsebeandefinitionelement 方法。不過這個方法只是表面,並未深入解析 中的 class 等屬性以及 property 等子標籤。
本文繼續跟進。
嗯,還是要耐著點性子,最好寫個 demo 打斷點跟蹤一下,這樣理解起來才更深刻。繼續看**:
public class beandefinitionparserdelegate
string parent = null;
// 讀取 標籤的 parent 屬性
if (ele.hasattribute(parent_attribute))
try
// catch ...}}
這裡才是真正解析 標籤內容的地方,比如常見的 class、parent、scope、lazy-init、autowire、property、constructor-arg 等,還有不常見的 lookup-method、replace-method 等。
該方法內部呼叫了乙個個方法去解析不同的標籤。這裡我們只跟進常見的 property 如何解析,其他方法大體也都差不多,有興趣可以自行研究。
parsepropertyelements 方法**如下:
public class beandefinitionparserdelegate
}} public void parsepropertyelement(element ele, beandefinition bd)
this.parsestate.push(new propertyentry(propertyname));
try
// 這裡解析得到的是 runtimebeanreference 或者 typedstrin**alue
object val = parsepropertyvalue(ele, bd, propertyname);
propertyvalue pv = new propertyvalue(propertyname, val);
parsemetaelements(ele, pv);
pv.setsource(extractsource(ele));
// 將解析到的值新增到 beandefinition 的屬性列表
bd.getpropertyvalues().addpropertyvalue(pv);
}finally
}
}
這個方法主要做了什麼呢?
遍歷節點並找到 property 標籤
解析 ref 和 value 的過程如下:
public class beandefinitionparserdelegate
else }}
// ref 和 value 屬性,二者不能並存
boolean hasrefattribute = ele.hasattribute(ref_attribute);
boolean hasvalueattribute = ele.hasattribute(value_attribute);
if ((hasrefattribute && hasvalueattribute) ||
((hasrefattribute || hasvalueattribute) && subelement != null))
// ref 屬性
if (hasrefattribute)
// 封裝為 runtimebeanreference 型別
runtimebeanreference ref = new runtimebeanreference(refname);
ref.setsource(extractsource(ele));
return ref;
}// value 屬性
else if (hasvalueattribute)
// 若還有子元素,繼續解析
else if (subelement != null)
else
}}
property 標籤的解析算是相對複雜的,其他標籤(meta、constructor-arg 等)的解析過程大體是類似的,不再一一分析。
經過 beandefinitionparserdelegate#parsebeandefinitionelement 方法的解析和封裝後,就得到了儲存我們自定義 bean 資訊的 beandefinition,即 genericbeandefinition。spring 又把 beandefinition 和別名資訊封裝成了 beandefinitionholder:
public class beandefinitionparserdelegate
return null;
}}
此外,在向 ioc 容器註冊之前,還有乙個 decoratebeandefinitionifrequired 方法,它主要是用來處理預設命名空間(即 之外的 bean 定義,比如 、等,這裡仍然先沿著主線走,暫不深入分析。
接下來就是將 beandefinition 註冊到 ioc 容器:
public class defaultbeandefinitiondocumentreader implements beandefinitiondocumentreader
catch (beandefinitionstoreexception ex)
// send registration event.
getreadercontext().firecomponentregistered(new beancomponentdefinition(bdholder));}}}
public abstract class beandefinitionreaderutils }}}
ioc 容器是哪個?如何註冊呢?
前文提到過,spring 預設的 ioc 容器是 defaultlistablebeanfactory,來看下它的繼承結構:
可以看到 defaultlistablebeanfactory 實現了 beandefinitionregistry 介面。
所謂的「註冊」到 ioc 容器,其實就是把 beandefinition 儲存到了 defaultlistablebeanfactory 持有的乙個 map 中,如下:
public class defaultlistablebeanfactory extends abstractautowirecapablebeanfactory
implements configurablelistablebeanfactory, beandefinitionregistry, serializable
catch (beandefinitionvalidationexception ex)
}// 獲取已存在的 beandefinition
beandefinition existingdefinition = this.beandefinitionmap.get(beanname);
if (existingdefinition != null)
// 這幾個異常資訊是不是有點眼熟?
else if (existingdefinition.getrole()
}else if (!beandefinition.equals(existingdefinition))
}else
}this.beandefinitionmap.put(beanname, beandefinition);
}else
}else
this.frozenbeandefinitionnames = null;
}if (existingdefinition != null || containssingleton(beanname))
else if (isconfigurationfrozen())
}}
上面幾個異常資訊是不是有點眼熟?
這個 beandefinitionmap 是個什麼呢?它就是個 map:
/** map of bean definition objects, keyed by bean name. */
private final mapbeandefinitionmap = new concurrenthashmap<>(256);
為了有個整體的把握,這裡把主要流程梳理成了乙個思維導圖:
其實前面幾篇文章主要是第乙個步驟,也就是「初始化 beanfactory,註冊 bean 定義」,而且只是沿著一條主線走下來的,其它細節部分有興趣的小夥伴可以自行研究。
ioc 容器已經建立,而且 beandefinition 也放進去了,如何從容器拿到我們想要的物件呢?
欲知後事如何,且聽下回分解~
咱也來體驗一下這個名片
DNS是如何解析的?
dns domain name system,負責將使用者請求的網域名稱解析為對應機器的ip位址。眾所周知,網際網路上的每一台機器的身份是由ip位址標識的,而我們想要與任何一台機器進行通訊都必須知道它的ip位址,然而,由於ip位址對人類來說難於記憶,因此就產生了網域名稱。就拿www.baidu.co...
Spring 是如何 解決迴圈依賴的問題
初次遇到這個問題是在開發中,但是沒有深究,前一陣參加面試就被問到這個問題,當時真是非常後悔,怎麼沒有好好研究一下呢。現在來亡羊補牢吧。迴圈依賴的定義 迴圈依賴就是迴圈引用,就是兩個或多個bean 相互之間的持有對方,比如circlea 引用circleb circleb 引用circlec,circ...
Spring是如何解決setter迴圈注入的
對於該問題,相信不少人面試中遇到過,原始碼也去看過,這裡我想說的是,看原始碼的方式。單純的去看原始碼是毫無意義的,看完了就忘了,最多也只是零星記得有那麼些東西,我覺得要想做的真正理解還是要帶著問題去看。假設現在有a b兩個類,a b相互迴圈依賴,那麼我們就帶著這個問題去找 首先getbean a 必...