溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

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

發(fā)布時(shí)間:2021-12-02 15:39:03 來(lái)源:億速云 閱讀:152 作者:柒染 欄目:大數(shù)據(jù)

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

我們接下來(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)注億速云行業(yè)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI