Spring源碼解析各種屬性的示例分析

這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)?lái)有關(guān)Spring源碼解析各種屬性的示例分析,文章內(nèi)容豐富且以專(zhuān)業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

成都創(chuàng)新互聯(lián)是專(zhuā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è)等專(zhuān)業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進(jìn)行浪卡子網(wǎng)站開(kāi)發(fā)網(wǎng)頁(yè)制作和功能擴(kuò)展;專(zhuān)業(yè)做搜索引擎喜愛(ài)的網(wǎng)站,專(zhuān)業(yè)的做網(wǎng)站團(tuán)隊(duì),希望更多企業(yè)前來(lái)合作!

我們接下來(lái)就來(lái)看下完整的解析過(guò)程。

1.解析方法回顧

上篇文章我們最終分析到下面這個(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è)方法的解析工作。

 

2.Description

首先是 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í)行的代碼:

Spring源碼解析各種屬性的示例分析  
 

3.parseMetaElements

這個(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 的父類(lèi),BeanMetadataAttributeAccessor 專(zhuān)門(mén)用來(lái)處理屬性的加載和讀取,相關(guān)介紹可以參考松哥前面的文章:

  • Spring 源碼第四彈!深入理解 BeanDefinition
 

4.parseLookupOverrideSubElements

這個(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 屬性中之后,我們也可以在代碼中查看:

Spring源碼解析各種屬性的示例分析  
 

5.parseReplacedMethodSubElements

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ù)類(lèi)型,所以在構(gòu)造完 ReplaceOverride 對(duì)象之后,接下來(lái)還要去解析 arg-type。

 

6.parseConstructorArgElements

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 入門(mén)教程,里邊有講。

我們來(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 方法中解析。

  1. 一開(kāi)始先去獲取 name,index 以及 value 屬性,因?yàn)闃?gòu)造方法的參數(shù)可以指定 name,也可以指定下標(biāo)。
  2. 先去判斷 index 是否有值,進(jìn)而決定按照 index 解析還是按照 name 解析。
  3. 無(wú)論哪種解析方式,都是通過(guò) parsePropertyValue 方法將 value 解析出來(lái)。
  4. 解析出來(lái)的子元素保存在 ConstructorArgumentValues.ValueHolder 對(duì)象中。
  5. 如果是通過(guò) index 來(lái)解析參數(shù),最終調(diào)用 addIndexedArgumentValue 方法保存解析結(jié)果,如果是通過(guò) name 來(lái)解析參數(shù),最終通過(guò) addGenericArgumentValue 方法來(lái)保存解析結(jié)果。
 

7.parsePropertyElements

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)的值。

 

8.parseQualifierElements

parseQualifierElements 就是用來(lái)解析 qualifier 節(jié)點(diǎn)的,最終也是保存在對(duì)應(yīng)的屬性中。解析過(guò)程和前面的類(lèi)似,我就不再贅述了,我們來(lái)看下解析結(jié)果:

Spring源碼解析各種屬性的示例分析  
 

9.再談 BeanDefinition

這里的屬性解析完了都是保存在 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 的生命周期、銷(xiāo)毀、初始化等等各種操作,我們定義的關(guān)于 Bean 的生命周期、銷(xiāo)毀、初始化等操作總得有一個(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)類(lèi)似于類(lèi)和對(duì)象的關(guān)系。

在 Spring 中,主要有三種類(lèi)型的 BeanDefinition:

  • RootBeanDefinition
  • ChildBeanDefinition
  • GenericBeanDefinition

在 Spring 中,如果我們?yōu)橐粋€(gè) Bean 配置了父 Bean,父 Bean 將被解析為 RootBeanDefinition,子 Bean 被解析為 ChildBeanDefinition,要是沒(méi)有父 Bean,則被解析為 RootBeanDefinition。

GenericBeanDefinition 是從 Spring2.5 以后新加入的 BeanDefinition 實(shí)現(xiàn)類(lèi)。GenericBeanDefinition 可以動(dòng)態(tài)設(shè)置父 Bean,同時(shí)兼具 RootBeanDefinition 和 ChildBeanDefinition 的功能。

目前普遍使用的就是 GenericBeanDefinition,所以我們看到前面的解析結(jié)果也是保存到 GenericBeanDefinition 中的。

上述就是小編為大家分享的Spring源碼解析各種屬性的示例分析了,如果剛好有類(lèi)似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。

文章題目:Spring源碼解析各種屬性的示例分析
分享鏈接:http://www.muchs.cn/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)

網(wǎng)站優(yōu)化排名