1. 程式人生 > 實用技巧 >Spring5原始碼分析(018)——IoC篇之解析bean標籤:註冊解析的BeanDefinition

Spring5原始碼分析(018)——IoC篇之解析bean標籤:註冊解析的BeanDefinition

注:《Spring5原始碼分析》彙總可參考:Spring5原始碼分析(002)——部落格彙總


  還是先回顧下bean標籤解析的核心處理邏輯:Spring5原始碼分析(011)——IoC篇之解析bean標籤總覽 ,程式碼如下

/**
 * Process the given bean element, parsing the bean definition
 * and registering it with the registry.
 * <p>處理給定的 bean 元素,解析 bean 定義並將其註冊到 registry 中
 */
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    
// 1、委託 BeanDefinitionParserDelegate 進行元素解析。解析成功,則返回的 BeanDefinitionHolder 例項 // 已經包含配置檔案中配置的各種屬性,例如 class 、 name 、 id 、 alias 之類的屬性。 // 解析失敗,則返回為 null BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { // 2、自定義標籤解析(若預設標籤下有自定義標籤) bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try { // 3、註冊 BeanDefinition // Register the final decorated instance. BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName()
+ "'", ele, ex); } // 4、發出響應事件,通知相關的監聽器,完成 bean 載入 // Send registration event. getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); } }

  這裡關於 bean 標籤的 4 個步驟處理,該解析也解析完了(第1步),該裝飾的也裝飾完了(第2步),對於得到的 BeanDefinition 已經可以滿足後續的使用要求了,接下來需要做的工作就是進行BeanDefinition 的註冊了,也即是第3步中提到的,通過 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()) 來註冊已經解析好的 BeanDefinition 。

  第4步中的通知監聽器解析及註冊完成是通過程式碼 getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)) 來進行處理的,這裡的實現只為擴充套件,當程式開發人員需要對註冊 BeanDefinition 事件進行監聽時可以通過註冊監聽器的方式並將處理邏輯寫入監聽器中,目前在 Spring 中並沒有對此事件做任何邏輯,就不再具體展開了。

  本文的目錄結構如下:

1、BeanDefinitionReaderUtils.registerBeanDefinition

  BeanDefinition 的註冊,是通過工具類 BeanDefinitionReaderUtils.registerBeanDefinition(...) 進行操作的,具體程式碼如下:

/**
 * Register the given bean definition with the given bean factory.
 * <p>註冊beanDefinition到給定bean工廠
 * @param definitionHolder the bean definition including name and aliases
 * @param registry the bean factory to register with
 * @throws BeanDefinitionStoreException if registration failed
 */
public static void registerBeanDefinition(
      BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
      throws BeanDefinitionStoreException {

   // Register bean definition under primary name.
   // 1、通過beanName作為唯一識別符號來註冊BeanDefinition
   String beanName = definitionHolder.getBeanName();
   registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

   // Register aliases for bean name, if any.
   // 2、註冊所有的別名:通過別名 alias 的註冊,即建立別名與 beanName 的對映關係
   String[] aliases = definitionHolder.getAliases();
   if (aliases != null) {
      for (String alias : aliases) {
         registry.registerAlias(beanName, alias);
      }
   }
}

  從上面的程式碼可以看出,解析完的 beanDefinition ,實際上都會被註冊到 BeanDefinitionRegistry 型別的例項 registry 中,這裡對於 beanDefinition 的註冊過程分為2部分:

  1. 通過 beanName 的註冊
  2. 通過別名 alias 的註冊,即建立別名與 beanName 的對映關係。

  下面將通過 BeanDefinitionRegistry 介面的實現對此進行詳細的解析。

2、BeanDefinitionRegistry

  beanDefinition 的註冊,其實是委託 org.springframework.beans.factory.support.BeanDefinitionRegistry 來進行處理,而相關的直接實現類就是我們前面說了N多遍的 DefaultListableBeanFactory

2.1、通過 beanName 的註冊

  對於 beanDefinition 的註冊,需要做的就是建立 beanName 與 beanDefinition 的對映關係,其實一個 map 就可以實現,事實上 Spring 中就是以 beanName 為 key,beanDefinition 為 value ,直接放入到 Map 中。這裡是通過呼叫 org.springframework.beans.factory.support.BeanDefinitionRegistry void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) 方法來實現通過 beanName 註冊 beanDefinition。具體實現在 DefaultListableBeanFactory 中,程式碼如下:

// org.springframework.beans.factory.support.DefaultListableBeanFactory

/** Whether to allow re-registration of a different definition with the same name. */
private boolean allowBeanDefinitionOverriding = true;

/** Names of beans that have already been created at least once. */
private final Set<String> alreadyCreated = Collections.newSetFromMap(new ConcurrentHashMap<>(256));

/** Map of bean definition objects, keyed by bean name. */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

/** List of bean definition names, in registration order. */
private volatile List<String> beanDefinitionNames = new ArrayList<>(256);

/** List of names of manually registered singletons, in registration order. */
private volatile Set<String> manualSingletonNames = new LinkedHashSet<>(16);

/** Cached array of bean definition names in case of frozen configuration. */
@Nullable
private volatile String[] frozenBeanDefinitionNames;

/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

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");

    if (beanDefinition instanceof AbstractBeanDefinition) {
        try {
            /*
             * 1、校驗 BeanDefinition
             * 這是註冊前的最後一次校驗,主要是對於 AbstractBeanDefinition 中的 methodOverrides 的校驗,
             * 校驗 methodOverrides 是否與工廠方法並存或者 methodOverrides 對應的方法根本不存在
             */
            ((AbstractBeanDefinition) beanDefinition).validate();
        }
        catch (BeanDefinitionValidationException ex) {
            throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                    "Validation of bean definition failed", ex);
        }
    }

    // 2、根據 beanName 獲取已註冊的 beanDefinition
    BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
    // 3、如果 beanName 對應的 beanDefinition 已存在
    if (existingDefinition != null) {
        // 3.1、如果已存在 beanDefinition 且配置了不允許覆蓋,則直接丟擲異常
        if (!isAllowBeanDefinitionOverriding()) {
            throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
        }
        // 原有 beanDefinition 的 role 小於新的 beanDefinition 的 role,記錄 info 級別日誌
        else if (existingDefinition.getRole() < beanDefinition.getRole()) {
            // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
            if (logger.isInfoEnabled()) {
                logger.info("Overriding user-defined bean definition for bean '" + beanName +
                        "' with a framework-generated bean definition: replacing [" +
                        existingDefinition + "] with [" + beanDefinition + "]");
            }
        }
        // 新的要註冊的 beanDefinition 與原有的 beanDefinition 不相同,記錄 debug 級別日誌
        else if (!beanDefinition.equals(existingDefinition)) {
            if (logger.isDebugEnabled()) {
                logger.debug("Overriding bean definition for bean '" + beanName +
                        "' with a different definition: replacing [" + existingDefinition +
                        "] with [" + beanDefinition + "]");
            }
        }
        else {
            if (logger.isTraceEnabled()) {
                logger.trace("Overriding bean definition for bean '" + beanName +
                        "' with an equivalent definition: replacing [" + existingDefinition +
                        "] with [" + beanDefinition + "]");
            }
        }
        // 3.2、允許直接覆蓋原有的 beanDefinition,直接覆蓋設定
        this.beanDefinitionMap.put(beanName, beanDefinition);
    }
    // 4、需要註冊的 beanName 尚未存在
    else {
        // 4.1、檢查 bean 建立階段是否已經開始,需要對 beanDefinitionMap 進行併發控制
        if (hasBeanCreationStarted()) {
            // Cannot modify startup-time collection elements anymore (for stable iteration)
            // 同步全域性變數beanDefinitionMap,防止併發訪問修改
            synchronized (this.beanDefinitionMap) {
                // 註冊 beanDefinition
                this.beanDefinitionMap.put(beanName, beanDefinition);
                // 將 beanName 新增到 beanDefinitionNames 中
                List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
                updatedDefinitions.addAll(this.beanDefinitionNames);
                updatedDefinitions.add(beanName);
                this.beanDefinitionNames = updatedDefinitions;
                // 從 manualSingletonNames 移除 beanName
                removeManualSingletonName(beanName);
            }
        }
        else {
            // Still in startup registration phase
            // 4.2、否則直接新增、移除
            this.beanDefinitionMap.put(beanName, beanDefinition);
            this.beanDefinitionNames.add(beanName);
            removeManualSingletonName(beanName);
        }
        this.frozenBeanDefinitionNames = null;
    }

    // 5、重新設定 beanName 對應的所有快取
    if (existingDefinition != null || containsSingleton(beanName)) {
        resetBeanDefinition(beanName);
    }
}

  從實現來看,處理過程主要分為以下幾個步驟:

  • 1、校驗 BeanDefinition ,這是註冊前的最後一次校驗,主要是對於 AbstractBeanDefinition 中的 methodOverrides 的校驗,校驗 methodOverrides 是否與工廠方法並存或者 methodOverrides 對應的方法根本不存在。
  • 2、根據 beanName 獲取已註冊的 beanDefinition
  • 3、如果 beanName 對應的 beanDefinition 已存在,則根據是否允許覆蓋的配置 allowBeanDefinitionOverriding 來決定是直接丟擲 BeanDefinitionOverrideException 異常(false 表示不允許覆蓋),還是直接覆蓋已有的 beanDefinition (true 表示允許覆蓋)。
  • 4、如果需要註冊的 beanName 尚未存在,則先檢查 bean 建立階段是否已經開始,如果是,則需要對 beanDefinitionMap 進行併發控制,然後將 (beanName, beanDefinition) 註冊到 beanDefinitionMap 中;否則直接註冊設定即可。
    • mark 一下,這裡的 hasBeanCreationStarted() ,後續再補充
  • 5、重新設定 beanName 對應的所有快取
    • 也先 mark 一下

看到這很明顯的,所謂的註冊基本就是放到 Map 裡面,即 (k, v) --> (beanName, beanDefinition)

2.2、通過別名alias註冊與beanName的對映

  別名 alias 的註冊,與 beanDefinition 的註冊類似,只是這裡是建立別名 alias 和 beanName 的對映,即 (alias, beanName) 。註冊別名呼叫的是 BeanDefinitionRegistry.registerAlias(String name, String alias),具體實際在 org.springframework.core.SimpleAliasRegistry 中,程式碼如下:

// org.springframework.core.SimpleAliasRegistry

public void registerAlias(String name, String alias) {
    Assert.hasText(name, "'name' must not be empty");
    Assert.hasText(alias, "'alias' must not be empty");
    synchronized (this.aliasMap) {
        // 1、如果 beanName 和 alias 相同的話不記錄 alias ,並移除對應的 alias
        if (alias.equals(name)) {
            this.aliasMap.remove(alias);
            if (logger.isDebugEnabled()) {
                logger.debug("Alias definition '" + alias + "' ignored since it points to same name");
            }
        }
        else {
            // 2、獲取 alias 已註冊的 beanName
            String registeredName = this.aliasMap.get(alias);
            if (registeredName != null) {
                // 2.1、已存在 alias 且對應的 registeredName 與 beanName 相同,不再重新註冊
                if (registeredName.equals(name)) {
                    // An existing alias - no need to re-register
                    return;
                }
                // 2.2、允許覆蓋 alias
                if (!allowAliasOverriding()) {
                    throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" +
                            name + "': It is already registered for name '" + registeredName + "'.");
                }
                if (logger.isDebugEnabled()) {
                    logger.debug("Overriding alias '" + alias + "' definition for registered name '" +
                            registeredName + "' with new target name '" + name + "'");
                }
            }
            // 3、alias 迴圈指向檢查
            checkForAliasCircle(name, alias);
            // 4、註冊 alias
            this.aliasMap.put(alias, name);
            if (logger.isTraceEnabled()) {
                logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'");
            }
        }
    }
}

/**
 * Check whether the given name points back to the given alias as an alias
 * in the other direction already, catching a circular reference upfront
 * and throwing a corresponding IllegalStateException.
 * @param name the candidate name
 * @param alias the candidate alias
 * @see #registerAlias
 * @see #hasAlias
 */
protected void checkForAliasCircle(String name, String alias) {
    if (hasAlias(alias, name)) {
        throw new IllegalStateException("Cannot register alias '" + alias +
                "' for name '" + name + "': Circular reference - '" +
                name + "' is a direct or indirect alias for '" + alias + "' already");
    }
}

/**
 * Determine whether the given name has the given alias registered.
 * <p>確定給定名稱是否已註冊給定別名
 * <p>這是 map 的迴圈指向判斷。例如需要註冊的name=1,alias=9,即aliasMap中的(alias, name) / (k,v) --> (9,1),
 * 此時入參就是(9,1),判斷類似以下幾種場景的迴圈指向:
 * <li>(9,1) --> (1,9)</li>
 * <li>(9,1) --> (2,9) --> (1,2)</li>
 * <li>(9,1) --> (2,9) --> (3,2) --> (1,3)</li>
 * <p>總結下:前面的map中的key作為下一個map的v,第一個map的v作為最後的key,即形成一個環
 * <p>這幾行簡單的程式碼可以用於判斷Map中已有的元素與即將put進去的(k,v)是否形成環
 * @param name the name to check
 * @param alias the alias to look for
 * @since 4.2.1
 */
public boolean hasAlias(String name, String alias) {
    for (Map.Entry<String, String> entry : this.aliasMap.entrySet()) {
        String registeredName = entry.getValue();
        if (registeredName.equals(name)) {
            String registeredAlias = entry.getKey();
            if (registeredAlias.equals(alias) || hasAlias(registeredAlias, alias)) {
                return true;
            }
        }
    }
    return false;
}

  alias 的註冊比較簡單,這裡需要注意的是對 alias 迴圈指向檢測,這裡再次簡單貼一下上面寫的註釋:前面的map中的key作為下一個map的v,第一個map的v作為最後的key,即形成一個環。就像註釋中提到的例子 (9,1) --> (2,9) --> (3,2) --> (1,3) ,反過來看就一目瞭然了:(1,3) --> (3,2) --> (2,9) --> (9,1) 。如果存在迴圈指向,則會直接排除 IllegalStateException 異常。

  至此,bean 標籤的解析方法 processBeanDefinition 所處理的工作已全部解析完成。

3、參考