這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)?lái)有關(guān)Spring源碼解析各種屬性的示例分析,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
成都創(chuàng)新互聯(lián)是專業(yè)的浪卡子網(wǎng)站建設(shè)公司,浪卡子接單;提供網(wǎng)站設(shè)計(jì)、成都網(wǎng)站制作,網(wǎng)頁(yè)設(shè)計(jì),網(wǎng)站設(shè)計(jì),建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進(jìn)行浪卡子網(wǎng)站開(kāi)發(fā)網(wǎng)頁(yè)制作和功能擴(kuò)展;專業(yè)做搜索引擎喜愛(ài)的網(wǎng)站,專業(yè)的做網(wǎng)站團(tuán)隊(duì),希望更多企業(yè)前來(lái)合作!
我們接下來(lái)就來(lái)看下完整的解析過(guò)程。
上篇文章我們最終分析到下面這個(gè)方法:
@Nullable
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
try {
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
parseMetaElements(ele, bd);
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
parseConstructorArgElements(ele, bd);
parsePropertyElements(ele, bd);
parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
return bd;
}
catch (ClassNotFoundException ex) {
error("Bean class [" + className + "] not found", ele, ex);
}
catch (NoClassDefFoundError err) {
error("Class that bean class [" + className + "] depends on not found", ele, err);
}
catch (Throwable ex) {
error("Unexpected failure during bean definition parsing", ele, ex);
}
finally {
this.parseState.pop();
}
return null;
}
parseBeanDefinitionAttributes 方法用來(lái)解析普通屬性,我們已經(jīng)在上篇文章中分析過(guò)了,這里不再贅述,今天主要來(lái)看看其他幾個(gè)方法的解析工作。
首先是 description 的解析,直接通過(guò) DomUtils.getChildElementValueByTagName 工具方法從節(jié)點(diǎn)中取出 description 屬性的值。這個(gè)沒(méi)啥好說(shuō)的。
小伙伴們?cè)诜治鲈创a時(shí),這些工具方法如果你不確定它的功能,或者想驗(yàn)證它的其他用法,可以通過(guò) IDEA 提供的 Evaluate Expression 功能現(xiàn)場(chǎng)調(diào)用該方法,進(jìn)而驗(yàn)證自己想法,就是下圖標(biāo)出來(lái)的那個(gè)計(jì)算器小圖標(biāo),點(diǎn)擊之后,輸入你想執(zhí)行的代碼:
這個(gè)方法主要是解析 meta 屬性。前面的視頻中已經(jīng)講了,這個(gè) meta 屬性是保存在 BeanDefinition 中的,也是從 BeanDefinition 中獲取的,按照這個(gè)思路,來(lái)看解析代碼就很容易懂了:
public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) {
NodeList nl = ele.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, META_ELEMENT)) {
Element metaElement = (Element) node;
String key = metaElement.getAttribute(KEY_ATTRIBUTE);
String value = metaElement.getAttribute(VALUE_ATTRIBUTE);
BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value);
attribute.setSource(extractSource(metaElement));
attributeAccessor.addMetadataAttribute(attribute);
}
}
}
可以看到,遍歷元素,從中提取出 meta 元素的值,并構(gòu)建出 BeanMetadataAttribute 對(duì)象,最后存入 GenericBeanDefinition 對(duì)象中。
有小伙伴說(shuō)不是存入 BeanMetadataAttributeAccessor 中嗎?這其實(shí)是 GenericBeanDefinition 的父類,BeanMetadataAttributeAccessor 專門用來(lái)處理屬性的加載和讀取,相關(guān)介紹可以參考松哥前面的文章:
這個(gè)方法是為了解析出 lookup-method 屬性,在前面的視頻中松哥已經(jīng)和大家聊過(guò),lookup-method 可以動(dòng)態(tài)替換運(yùn)行的方法,按照這個(gè)思路,我們來(lái)看下這個(gè)方法的源碼:
public void parseLookupOverrideSubElements(Element beanEle, MethodOverrides overrides) {
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, LOOKUP_METHOD_ELEMENT)) {
Element ele = (Element) node;
String methodName = ele.getAttribute(NAME_ATTRIBUTE);
String beanRef = ele.getAttribute(BEAN_ELEMENT);
LookupOverride override = new LookupOverride(methodName, beanRef);
override.setSource(extractSource(ele));
overrides.addOverride(override);
}
}
}
可以看到,在這里遍歷元素,從 lookup-method 屬性中,取出來(lái) methodName 和 beanRef 屬性,構(gòu)造出 LookupOverride 然后存入 GenericBeanDefinition 的 methodOverrides 屬性中。
存入 GenericBeanDefinition 的 methodOverrides 屬性中之后,我們也可以在代碼中查看:
parseReplacedMethodSubElements 這個(gè)方法主要是解析 replace-method 屬性的,根據(jù)前面視頻的講解,replace-method 可以實(shí)現(xiàn)動(dòng)態(tài)替換方法,并且可以在替換時(shí)修改方法。
按照這個(gè)思路,該方法就很好理解了:
public void parseReplacedMethodSubElements(Element beanEle, MethodOverrides overrides) {
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, REPLACED_METHOD_ELEMENT)) {
Element replacedMethodEle = (Element) node;
String name = replacedMethodEle.getAttribute(NAME_ATTRIBUTE);
String callback = replacedMethodEle.getAttribute(REPLACER_ATTRIBUTE);
ReplaceOverride replaceOverride = new ReplaceOverride(name, callback);
// Look for arg-type match elements.
List<Element> argTypeEles = DomUtils.getChildElementsByTagName(replacedMethodEle, ARG_TYPE_ELEMENT);
for (Element argTypeEle : argTypeEles) {
String match = argTypeEle.getAttribute(ARG_TYPE_MATCH_ATTRIBUTE);
match = (StringUtils.hasText(match) ? match : DomUtils.getTextValue(argTypeEle));
if (StringUtils.hasText(match)) {
replaceOverride.addTypeIdentifier(match);
}
}
replaceOverride.setSource(extractSource(replacedMethodEle));
overrides.addOverride(replaceOverride);
}
}
}
name 獲取到的是要替換的舊方法,callback 則是獲取到的要替換的新方法,接下來(lái)再去構(gòu)造 ReplaceOverride 對(duì)象。
另外由于 replace-method 內(nèi)部還可以再配置參數(shù)類型,所以在構(gòu)造完 ReplaceOverride 對(duì)象之后,接下來(lái)還要去解析 arg-type。
parseConstructorArgElements 這個(gè)方法主要是用來(lái)解析構(gòu)造方法的。這個(gè)大家日常開(kāi)發(fā)中應(yīng)該接觸的很多。如果小伙伴們對(duì)于各種各樣的構(gòu)造方法注入還不太熟悉,可以在微信公眾號(hào)江南一點(diǎn)雨后臺(tái)回復(fù) spring5,獲取松哥之前錄制的免費(fèi) Spring 入門教程,里邊有講。
我們來(lái)看下構(gòu)造方法的解析:
public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) {
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, CONSTRUCTOR_ARG_ELEMENT)) {
parseConstructorArgElement((Element) node, bd);
}
}
}
public void parseConstructorArgElement(Element ele, BeanDefinition bd) {
String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE);
String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
if (StringUtils.hasLength(indexAttr)) {
try {
int index = Integer.parseInt(indexAttr);
if (index < 0) {
error("'index' cannot be lower than 0", ele);
}
else {
try {
this.parseState.push(new ConstructorArgumentEntry(index));
Object value = parsePropertyValue(ele, bd, null);
ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
if (StringUtils.hasLength(typeAttr)) {
valueHolder.setType(typeAttr);
}
if (StringUtils.hasLength(nameAttr)) {
valueHolder.setName(nameAttr);
}
valueHolder.setSource(extractSource(ele));
if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) {
error("Ambiguous constructor-arg entries for index " + index, ele);
}
else {
bd.getConstructorArgumentValues().addIndexedArgumentValue(index, valueHolder);
}
}
finally {
this.parseState.pop();
}
}
}
catch (NumberFormatException ex) {
error("Attribute 'index' of tag 'constructor-arg' must be an integer", ele);
}
}
else {
try {
this.parseState.push(new ConstructorArgumentEntry());
Object value = parsePropertyValue(ele, bd, null);
ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
if (StringUtils.hasLength(typeAttr)) {
valueHolder.setType(typeAttr);
}
if (StringUtils.hasLength(nameAttr)) {
valueHolder.setName(nameAttr);
}
valueHolder.setSource(extractSource(ele));
bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
}
finally {
this.parseState.pop();
}
}
}
可以看到,構(gòu)造函數(shù)最終在 parseConstructorArgElement 方法中解析。
parsePropertyElements 方法用來(lái)解析屬性注入。
public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
parsePropertyElement((Element) node, bd);
}
}
}
public void parsePropertyElement(Element ele, BeanDefinition bd) {
String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
if (!StringUtils.hasLength(propertyName)) {
error("Tag 'property' must have a 'name' attribute", ele);
return;
}
this.parseState.push(new PropertyEntry(propertyName));
try {
if (bd.getPropertyValues().contains(propertyName)) {
error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
return;
}
Object val = parsePropertyValue(ele, bd, propertyName);
PropertyValue pv = new PropertyValue(propertyName, val);
parseMetaElements(ele, pv);
pv.setSource(extractSource(ele));
bd.getPropertyValues().addPropertyValue(pv);
}
finally {
this.parseState.pop();
}
}
前面看了那么多,再看這個(gè)方法就比較簡(jiǎn)單了。這里最終還是通過(guò) parsePropertyValue 方法解析出 value,并調(diào)用 addPropertyValue 方法來(lái)存入相關(guān)的值。
parseQualifierElements 就是用來(lái)解析 qualifier 節(jié)點(diǎn)的,最終也是保存在對(duì)應(yīng)的屬性中。解析過(guò)程和前面的類似,我就不再贅述了,我們來(lái)看下解析結(jié)果:
這里的屬性解析完了都是保存在 GenericBeanDefinition 對(duì)象中,而該對(duì)象將來(lái)可以用來(lái)構(gòu)建一個(gè) Bean。
在 Spring 容器中,我們廣泛使用的是一個(gè)一個(gè)的 Bean,BeanDefinition 從名字上就可以看出是關(guān)于 Bean 的定義。
事實(shí)上就是這樣,我們?cè)?XML 文件中配置的 Bean 的各種屬性,這些屬性不僅僅是和對(duì)象相關(guān),Spring 容器還要解決 Bean 的生命周期、銷毀、初始化等等各種操作,我們定義的關(guān)于 Bean 的生命周期、銷毀、初始化等操作總得有一個(gè)對(duì)象來(lái)承載,那么這個(gè)對(duì)象就是 BeanDefinition。
XML 中定義的各種屬性都會(huì)先加載到 BeanDefinition 上,然后通過(guò) BeanDefinition 來(lái)生成一個(gè) Bean,從這個(gè)角度來(lái)說(shuō),BeanDefinition 和 Bean 的關(guān)系有點(diǎn)類似于類和對(duì)象的關(guān)系。
在 Spring 中,主要有三種類型的 BeanDefinition:
在 Spring 中,如果我們?yōu)橐粋€(gè) Bean 配置了父 Bean,父 Bean 將被解析為 RootBeanDefinition,子 Bean 被解析為 ChildBeanDefinition,要是沒(méi)有父 Bean,則被解析為 RootBeanDefinition。
GenericBeanDefinition 是從 Spring2.5 以后新加入的 BeanDefinition 實(shí)現(xiàn)類。GenericBeanDefinition 可以動(dòng)態(tài)設(shè)置父 Bean,同時(shí)兼具 RootBeanDefinition 和 ChildBeanDefinition 的功能。
目前普遍使用的就是 GenericBeanDefinition,所以我們看到前面的解析結(jié)果也是保存到 GenericBeanDefinition 中的。
上述就是小編為大家分享的Spring源碼解析各種屬性的示例分析了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。
文章題目:Spring源碼解析各種屬性的示例分析
分享鏈接:http://jinyejixie.com/article34/gceise.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站設(shè)計(jì)公司、網(wǎng)站內(nèi)鏈、App設(shè)計(jì)、ChatGPT、靜態(tài)網(wǎng)站、網(wǎng)站收錄
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)