真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

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

這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)碛嘘P(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)站開發(fā)網(wǎng)頁(yè)制作和功能擴(kuò)展;專業(yè)做搜索引擎喜愛的網(wǎng)站,專業(yè)的做網(wǎng)站團(tuán)隊(duì),希望更多企業(yè)前來合作!

我們接下來就來看下完整的解析過程。

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 方法用來解析普通屬性,我們已經(jīng)在上篇文章中分析過了,這里不再贅述,今天主要來看看其他幾個(gè)方法的解析工作。

 

2.Description

首先是 description 的解析,直接通過 DomUtils.getChildElementValueByTagName 工具方法從節(jié)點(diǎn)中取出 description 屬性的值。這個(gè)沒啥好說的。

小伙伴們?cè)诜治鲈创a時(shí),這些工具方法如果你不確定它的功能,或者想驗(yàn)證它的其他用法,可以通過 IDEA 提供的 Evaluate Expression 功能現(xiàn)場(chǎng)調(diào)用該方法,進(jìn)而驗(yàn)證自己想法,就是下圖標(biāo)出來的那個(gè)計(jì)算器小圖標(biāo),點(diǎn)擊之后,輸入你想執(zhí)行的代碼:

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

3.parseMetaElements

這個(gè)方法主要是解析 meta 屬性。前面的視頻中已經(jīng)講了,這個(gè) meta 屬性是保存在 BeanDefinition 中的,也是從 BeanDefinition 中獲取的,按照這個(gè)思路,來看解析代碼就很容易懂了:

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ì)象中。

有小伙伴說不是存入 BeanMetadataAttributeAccessor 中嗎?這其實(shí)是 GenericBeanDefinition 的父類,BeanMetadataAttributeAccessor 專門用來處理屬性的加載和讀取,相關(guān)介紹可以參考松哥前面的文章:

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

4.parseLookupOverrideSubElements

這個(gè)方法是為了解析出 lookup-method 屬性,在前面的視頻中松哥已經(jīng)和大家聊過,lookup-method 可以動(dòng)態(tài)替換運(yùn)行的方法,按照這個(gè)思路,我們來看下這個(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 屬性中,取出來 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 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 則是獲取到的要替換的新方法,接下來再去構(gòu)造 ReplaceOverride 對(duì)象。

另外由于 replace-method 內(nèi)部還可以再配置參數(shù)類型,所以在構(gòu)造完 ReplaceOverride 對(duì)象之后,接下來還要去解析 arg-type。

 

6.parseConstructorArgElements

parseConstructorArgElements 這個(gè)方法主要是用來解析構(gòu)造方法的。這個(gè)大家日常開發(fā)中應(yīng)該接觸的很多。如果小伙伴們對(duì)于各種各樣的構(gòu)造方法注入還不太熟悉,可以在微信公眾號(hào)江南一點(diǎn)雨后臺(tái)回復(fù) spring5,獲取松哥之前錄制的免費(fèi) Spring 入門教程,里邊有講。

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

7.parsePropertyElements

parsePropertyElements 方法用來解析屬性注入。

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)單了。這里最終還是通過 parsePropertyValue 方法解析出 value,并調(diào)用 addPropertyValue 方法來存入相關(guān)的值。

 

8.parseQualifierElements

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

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

9.再談 BeanDefinition

這里的屬性解析完了都是保存在 GenericBeanDefinition 對(duì)象中,而該對(duì)象將來可以用來構(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ì)象來承載,那么這個(gè)對(duì)象就是 BeanDefinition。

XML 中定義的各種屬性都會(huì)先加載到 BeanDefinition 上,然后通過 BeanDefinition 來生成一個(gè) Bean,從這個(gè)角度來說,BeanDefinition 和 Bean 的關(guān)系有點(diǎn)類似于類和對(duì)象的關(guān)系。

在 Spring 中,主要有三種類型的 BeanDefinition:

  • RootBeanDefinition
  • ChildBeanDefinition
  • GenericBeanDefinition

在 Spring 中,如果我們?yōu)橐粋€(gè) Bean 配置了父 Bean,父 Bean 將被解析為 RootBeanDefinition,子 Bean 被解析為 ChildBeanDefinition,要是沒有父 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è)資訊頻道。


文章標(biāo)題:Spring源碼解析各種屬性的示例分析
標(biāo)題來源:http://weahome.cn/article/gceise.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部