中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

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

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

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

發布時間:2021-12-02 15:39:03 來源:億速云 閱讀:158 作者:柒染 欄目:大數據

這期內容當中小編將會給大家帶來有關Spring源碼解析各種屬性的示例分析,文章內容豐富且以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

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

1.解析方法回顧

上篇文章我們最終分析到下面這個方法:

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

 

2.Description

首先是 description 的解析,直接通過 DomUtils.getChildElementValueByTagName 工具方法從節點中取出 description 屬性的值。這個沒啥好說的。

小伙伴們在分析源碼時,這些工具方法如果你不確定它的功能,或者想驗證它的其他用法,可以通過 IDEA 提供的 Evaluate Expression 功能現場調用該方法,進而驗證自己想法,就是下圖標出來的那個計算器小圖標,點擊之后,輸入你想執行的代碼:

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

3.parseMetaElements

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

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 元素的值,并構建出 BeanMetadataAttribute 對象,最后存入 GenericBeanDefinition 對象中。

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

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

4.parseLookupOverrideSubElements

這個方法是為了解析出 lookup-method 屬性,在前面的視頻中松哥已經和大家聊過,lookup-method 可以動態替換運行的方法,按照這個思路,我們來看下這個方法的源碼:

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 屬性,構造出 LookupOverride 然后存入 GenericBeanDefinition 的 methodOverrides 屬性中。

存入 GenericBeanDefinition 的 methodOverrides 屬性中之后,我們也可以在代碼中查看:

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

5.parseReplacedMethodSubElements

parseReplacedMethodSubElements 這個方法主要是解析 replace-method 屬性的,根據前面視頻的講解,replace-method 可以實現動態替換方法,并且可以在替換時修改方法。

按照這個思路,該方法就很好理解了:

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 則是獲取到的要替換的新方法,接下來再去構造 ReplaceOverride 對象。

另外由于 replace-method 內部還可以再配置參數類型,所以在構造完 ReplaceOverride 對象之后,接下來還要去解析 arg-type。

 

6.parseConstructorArgElements

parseConstructorArgElements 這個方法主要是用來解析構造方法的。這個大家日常開發中應該接觸的很多。如果小伙伴們對于各種各樣的構造方法注入還不太熟悉,可以在微信公眾號江南一點雨后臺回復 spring5,獲取松哥之前錄制的免費 Spring 入門教程,里邊有講。

我們來看下構造方法的解析:

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();
  }
 }
}
 

可以看到,構造函數最終在 parseConstructorArgElement 方法中解析。

  1. 一開始先去獲取 name,index 以及 value 屬性,因為構造方法的參數可以指定 name,也可以指定下標。
  2. 先去判斷 index 是否有值,進而決定按照 index 解析還是按照 name 解析。
  3. 無論哪種解析方式,都是通過 parsePropertyValue 方法將 value 解析出來。
  4. 解析出來的子元素保存在 ConstructorArgumentValues.ValueHolder 對象中。
  5. 如果是通過 index 來解析參數,最終調用 addIndexedArgumentValue 方法保存解析結果,如果是通過 name 來解析參數,最終通過 addGenericArgumentValue 方法來保存解析結果。
 

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();
 }
}
 

前面看了那么多,再看這個方法就比較簡單了。這里最終還是通過 parsePropertyValue 方法解析出 value,并調用 addPropertyValue 方法來存入相關的值。

 

8.parseQualifierElements

parseQualifierElements 就是用來解析 qualifier 節點的,最終也是保存在對應的屬性中。解析過程和前面的類似,我就不再贅述了,我們來看下解析結果:

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

9.再談 BeanDefinition

這里的屬性解析完了都是保存在 GenericBeanDefinition 對象中,而該對象將來可以用來構建一個 Bean。

在 Spring 容器中,我們廣泛使用的是一個一個的 Bean,BeanDefinition 從名字上就可以看出是關于 Bean 的定義。

事實上就是這樣,我們在 XML 文件中配置的 Bean 的各種屬性,這些屬性不僅僅是和對象相關,Spring 容器還要解決 Bean 的生命周期、銷毀、初始化等等各種操作,我們定義的關于 Bean 的生命周期、銷毀、初始化等操作總得有一個對象來承載,那么這個對象就是 BeanDefinition。

XML 中定義的各種屬性都會先加載到 BeanDefinition 上,然后通過 BeanDefinition 來生成一個 Bean,從這個角度來說,BeanDefinition 和 Bean 的關系有點類似于類和對象的關系。

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

  • RootBeanDefinition
  • ChildBeanDefinition
  • GenericBeanDefinition

在 Spring 中,如果我們為一個 Bean 配置了父 Bean,父 Bean 將被解析為 RootBeanDefinition,子 Bean 被解析為 ChildBeanDefinition,要是沒有父 Bean,則被解析為 RootBeanDefinition。

GenericBeanDefinition 是從 Spring2.5 以后新加入的 BeanDefinition 實現類。GenericBeanDefinition 可以動態設置父 Bean,同時兼具 RootBeanDefinition 和 ChildBeanDefinition 的功能。

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

上述就是小編為大家分享的Spring源碼解析各種屬性的示例分析了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

邵东县| 钟山县| 焉耆| 隆化县| 衢州市| 阿克苏市| 吉安市| 化德县| 城固县| 阳山县| 中阳县| 子洲县| 高尔夫| 襄汾县| 霍林郭勒市| 四平市| 辰溪县| 宜黄县| 广饶县| 枣庄市| 灯塔市| 牙克石市| 石楼县| 比如县| 莎车县| 新田县| 浙江省| 金平| 南部县| 寿宁县| 洛浦县| 茌平县| 叶城县| 永康市| 饶平县| 正定县| 饶河县| 炎陵县| 当阳市| 康定县| 芦山县|