Spring 是如何解析 標籤的?

2021-10-19 13:19:43 字數 4663 閱讀 2992

上回「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 必...