Spring源碼閱讀之如何將標籤解析成BeanDefinition定義

上一篇文章我們講到了BeanFactory的創建和BeanDefinition的創建。本文接着上一篇文章,來看看一個<bean>標籤是如何被解析成BeanDefinition對象並註冊到BeanFactory裏面

  1. 爲了方便講解,在開始<bean>標籤解析前我們稍微對測試代碼做一點小調整,在配置文件中新增兩個<property>屬性,對應的userService相應的增加兩個對應的屬性字段。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd"
>
    <bean id="userService" class="UserServiceImpl">
        // userService增加兩個屬性
        <property name="name" value="z小趙"/>
        <property name="age" value="27"/>
    </bean>
</beans>
// UserServiceImpl增加兩個字段,並聲明瞭對應的set方法
public class UserServiceImpl implements UserService {
    private String name;
    private int age;
    @Override
    public void save(String name, int age) {
        System.out.println("name:" + name + ",age:" + age);
    }

    public void setName(String name) {
        this.name = name;
    }
    public void setAge(int age) {
        this.age = age;
    }
}

  1. 遍歷 Document 的所有子節點,針對不同的標籤執行不同的解析邏輯,我們側重來看下平時最常用的<bean>標籤是怎麼被解析的。

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
    // Step1:如果 “import” 標籤
    if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
      importBeanDefinitionResource(ele);
    }
    // Step2:如果是 “alias” 標籤
    else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
      processAliasRegistration(ele);
    }
    // Step3:如果是 “bean” 標籤
    else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
      processBeanDefinition(ele, delegate);
    }
    // Step4:如果是 “beans” 標籤
    else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
      // recurse
      doRegisterBeanDefinitions(ele);
    }
}

  1. 看看processBeanDefinition是怎麼把<bean>標籤解析成 BeanDefinition 並註冊到 BeanFactory 裏,重點在第一步解析<bean>標籤和第三步註冊BeanDefinition

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    // Step1:創建一個BeanDefinitionHolder,從名字看就是創建了一個BeanDefinition對象持有者
    // 它包含了創建出來的BeanDefinition定義,還有beanName等等屬性
    // 最後會從這個對象裏面獲取beanName和BeanDefinition定義,並將其註冊到BeanFactory裏面
    BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
    if (bdHolder != null) {
      // Step2:對創建出來的BeanDefinitionHolder做一些裝飾操作,如果有必要的話.
      // 比如 “autowire" "id" "lazy-init"等等node做一些額外的裝飾操作
      // 還有,如果當前element還有子節點並且子節點也是element,則對子節點進行裝飾操作
      bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
      try {
        // Register the final decorated instance.
        // Step3:註冊BeanDefinition到BeanFactory裏面
        BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
      }
      catch (BeanDefinitionStoreException ex) {
        getReaderContext().error("Failed to register bean definition with name '" +
            bdHolder.getBeanName() + "'", ele, ex);
      }
      // Send registration event.
      // Step4:發送一個註冊完成事件
      getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
    }
}

  1. 接着上面先來看第一步<bean>標籤是怎麼被解析成BeanDefinition對象的。

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
    // Step1:獲取當前bean標籤的id和name(這個name其實就是我們在xml文件裏面指定的別名)屬性
    String id = ele.getAttribute(ID_ATTRIBUTE);
    String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
    // Step2:解析別名,我們沒有配置,所以不會走這裏
    List<String> aliases = new ArrayList<>();
    if (StringUtils.hasLength(nameAttr)) {
      String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
      aliases.addAll(Arrays.asList(nameArr));
    }

    // Step3:默認使用id作爲beanName,beanName就是我們在代碼裏面指定要獲取那個,Bean實例的名字
    String beanName = id;
    // 這裏是如果沒有指定id && 指定了name屬性,則使用別名作爲beanName
    // 所以從這裏也可以看出,如果同時指定id和name,beanName還是會優先用id指定的名字
    if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
      beanName = aliases.remove(0);
      if (logger.isDebugEnabled()) {
        logger.debug("No XML 'id' specified - using '" + beanName +
            "' as bean name and " + aliases + " as aliases");
      }
    }

    if (containingBean == null) {
      // Step4:檢查beanName和alias是否是唯一的。
      // 如果發現在之前已經初始化了一個相同名字的bean定義,則拋出異常
      // 反之,將當前的beanName添加到BeanDefinitionParseDelegate裏面
      checkNameUniqueness(beanName, aliases, ele);
    }

    // Step5:根據element構建出BeanDefinition對象,前面一直說的BeanDefinition對象就是在這個方法裏面創建出來的
    // 後面會對這個方法展開具體講解
    AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
    // Step6:如果創建BeanDefinition對象的時候報錯有可能會返回null
    if (beanDefinition != null) {
      // Step6.1:如果當前beanName爲空,
      if (!StringUtils.hasText(beanName)) {
        try {
          // Step6.1.1:containingBean不爲null,則從containingBean定義生成一個beanName, 一般這裏走不到,因爲我們在xml裏面都會指定id或者name屬性
          if (containingBean != null) {
            beanName = BeanDefinitionReaderUtils.generateBeanName(
                beanDefinition, this.readerContext.getRegistry(), true);
          }
          else {
            // Step6.1.2:否則利用BeanDefinition定義生成一個beanName
            beanName = this.readerContext.generateBeanName(beanDefinition);
            // Register an alias for the plain bean class name, if still possible,
            // if the generator returned the class name plus a suffix.
            // This is expected for Spring 1.2/2.0 backwards compatibility.
            String beanClassName = beanDefinition.getBeanClassName();
            if (beanClassName != null &&
                beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
                !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
              aliases.add(beanClassName);
            }
          }
          if (logger.isDebugEnabled()) {
            logger.debug("Neither XML 'id' nor 'name' specified - " +
                "using generated bean name [" + beanName + "]");
          }
        }
        catch (Exception ex) {
          error(ex.getMessage(), ele);
          return null;
        }
      }
      // Step6.2:將創建出來的BeanDefinition定義、beanName、alias等封裝成一個BeanDefinitionHolder返回
      String[] aliasesArray = StringUtils.toStringArray(aliases);
      return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
    }

    return null;
}

  1. 在進一步深入看看根據 element 是如何創建出BeanDefinition對象的,即parseBeanDefinitionElement方法的具體實現邏輯是怎樣的。

public AbstractBeanDefinition parseBeanDefinitionElement(
      Element ele, String beanName, @Nullable BeanDefinition containingBean) {

    this.parseState.push(new BeanEntry(beanName));

    // Step1:首先獲取class屬性值,如果存在的話
    String className = null;
    if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
      className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
    }
    // Step2:獲取parent屬性,如果指定了的話,我們在xml裏面定義的時候可以指定parent屬性。感興趣的可以試試
    String parent = null;
    if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
      parent = ele.getAttribute(PARENT_ATTRIBUTE);
    }

    try {
      // Step3:根據當前element對應的className和其對應的父name,創建一個GenericBeanDefintion對象,
      // 因爲GenricBeanDefintion繼承了AbstractBeanDefinition,
      // 所以返回了AbstractBeanDefinition對象
      AbstractBeanDefinition bd = createBeanDefinition(className, parent);
      // Step4:有了BeanDefinition定義後,開始解析一些其他屬性
      // 比如是否指定了 init-method方法、destory-method方法、depends-on等等屬性
      // 如果指定了,則把指定的相關信息設置到BeanDefinition定義上
      parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
      // Step5:設置描述
      bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
      // Step6:解析元數據
      parseMetaElements(ele, bd);
      // Step7:解析 override 的方法
      parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
      // Step8:解析 replaced-method 的方法
      parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
      // Step9:解析構造器的參數,及 constructor-arg標籤
      parseConstructorArgElements(ele, bd);
      // Step10:解析property屬性,其實就是解析我們的測試例子指定了下面的這個屬性的,其他的解析類似。感興趣的可以自己研究
      // <property name="name" value="z小趙">
      // <property name="age" value="27">
      // 將解析後的結果構建成一個個PropertyValue對象,
      // 然後將該對象設置到BeanDefinition的property字段上
      parsePropertyElements(ele, bd);
      // Step11:解析qualifier屬性
      parseQualifierElements(ele, bd);

      bd.setResource(this.readerContext.getResource());
      bd.setSource(extractSource(ele));
      // Step12:經過上面一系列的填充之後獲得BeanDefinition對象,然後將其返回
      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;
}

  1. 到這裏我們創建出了BeanDefinition對象,然後接着第 3 步,對創建出來的BeanDefinition對象在做一些裝飾操作,如果有必要的話。具體在這裏就不深究了,感興趣的同學自行研究一下。我們重點來看下對經過一系列操作後得到最終的BeanDefinition定義是怎麼被註冊到BeanFactory裏面的。

public static void registerBeanDefinition(
      BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
      throws BeanDefinitionStoreException {

    // Register bean definition under primary name.
    // Step1:獲取當前BeanDefinition對應的beanName,
    // 然後使用registry將BeanDefinition註冊到BeanFactory裏面
    String beanName = definitionHolder.getBeanName();
    registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

    // Register aliases for bean name, if any.
    // Step2:如果有別名的,同時也將別名註冊到BeanFactory裏面
    String[] aliases = definitionHolder.getAliases();
    if (aliases != null) {
      for (String alias : aliases) {
        registry.registerAlias(beanName, alias);
      }
    }
}

  1. 那我們重點看下如何通過registryBeanDefinition定義註冊到BeanFactory裏面,因爲我們最開始的時候創建了BeanFactoryDefaultListableBeanFactory,所以這裏調用了DefaultListableBeanFactoryregistryBeanDefinition方法進行註冊的。

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
      throws BeanDefinitionStoreException {

    Assert.hasText(beanName, "Bean name must not be empty");
    Assert.notNull(beanDefinition, "BeanDefinition must not be null");
    // Step1:當前BeanDefinition是否實現或繼承了AbstractBeanDefinition對象
    // 從我們上面分析代碼可以看出是繼承了AbstractBeanDefinition類的
    if (beanDefinition instanceof AbstractBeanDefinition) {
      try {
        // Step1.1:對將要註冊的BeanDefinition定義進行校驗操作。
        // 其實主要校驗的就是提前指定了classLoader的那種bean
        ((AbstractBeanDefinition) beanDefinition).validate();
      }
      catch (BeanDefinitionValidationException ex) {
        throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
            "Validation of bean definition failed", ex);
      }
    }

    BeanDefinition oldBeanDefinition;
    // Step2:根據beanName獲取舊的BeanDefinition,如果已經存在了並且不允許被覆蓋的話,就拋出異常。正常情況下這個oldBeanDefinition是爲null的
    oldBeanDefinition = this.beanDefinitionMap.get(beanName);
    if (oldBeanDefinition != null) {
      if (!isAllowBeanDefinitionOverriding()) {
        throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
            "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
            "': There is already [" + oldBeanDefinition + "] bound.");
      }
      else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
        // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
        if (this.logger.isWarnEnabled()) {
          this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
              "' with a framework-generated bean definition: replacing [" +
              oldBeanDefinition + "] with [" + beanDefinition + "]");
        }
      }
      else if (!beanDefinition.equals(oldBeanDefinition)) {
        if (this.logger.isInfoEnabled()) {
          this.logger.info("Overriding bean definition for bean '" + beanName +
              "' with a different definition: replacing [" + oldBeanDefinition +
              "] with [" + beanDefinition + "]");
        }
      }
      else {
        if (this.logger.isDebugEnabled()) {
          this.logger.debug("Overriding bean definition for bean '" + beanName +
              "' with an equivalent definition: replacing [" + oldBeanDefinition +
              "] with [" + beanDefinition + "]");
        }
      }
      this.beanDefinitionMap.put(beanName, beanDefinition);
    }
    else {
      // Step3:檢查一下bean的創建工作是否已經開始,這裏大白話解釋一下
      // 從else的註釋可以看出,這裏其實想表達的意思應該是BeanFactory創建BeanDefinition定義的階段已經完成了
      // 但是此時還想再註冊BeanDefinition定義,則走if條件。如有理解不對請指正
      if (hasBeanCreationStarted()) {
        // Cannot modify startup-time collection elements anymore (for stable iteration)
        synchronized (this.beanDefinitionMap) {
          this.beanDefinitionMap.put(beanName, beanDefinition);
          List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
          updatedDefinitions.addAll(this.beanDefinitionNames);
          updatedDefinitions.add(beanName);
          this.beanDefinitionNames = updatedDefinitions;
          if (this.manualSingletonNames.contains(beanName)) {
            Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
            updatedSingletons.remove(beanName);
            this.manualSingletonNames = updatedSingletons;
          }
        }
      }
      else {
        // Step4:正常註冊流程走的else,因爲當前階段正是在啓動BeanFactory和註冊BeanDefinition的時候
        // 從代碼可以看出註冊其實就是將創建出來的BeanDefinition放到BeanFactory的map裏面,就這麼簡單而已
        // Still in startup registration phase
        this.beanDefinitionMap.put(beanName, beanDefinition);
        this.beanDefinitionNames.add(beanName);
        this.manualSingletonNames.remove(beanName);
      }
      this.frozenBeanDefinitionNames = null;
    }

    if (oldBeanDefinition != null || containsSingleton(beanName)) {
      resetBeanDefinition(beanName);
    }
}

總結

到此,我們關於BeanFactory創建,BeanDefinition定義創建及註冊工作就全部做完了,關於 <import><benas> 標籤的解析處理工作,大致流程都是差不多的,就不在一一展開了,有興趣的同學可以自行閱讀以下,只要搞懂了<bean>的構建流程,相信關於其他標籤的構建也就比較容易了。

接下來兩篇文章我們接着講和我們常見的<bean>標籤解析不同的另外一種自定義標籤,比如<cotext:component-scan>標籤的實現原理;講完非默認標籤解析後,講講如果想實現一個自定義標籤,那該怎麼做呢?相信大家見過各種各樣的自定義標籤名,比如如果你用過motan的話,會遇到過類似如下的這種標籤。如果想知道這種自定義標籤是怎麼被解析的,那就跟着我繼續深入學習吧。

<motan:referer id="userService" interface="com.xxx.UserService">

歡迎關注我,一起學習

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章