1. 程式人生 > >【死磕 Spring】----- IOC 之註冊解析的 BeanDefinition

【死磕 Spring】----- IOC 之註冊解析的 BeanDefinition

DefaultBeanDefinitionDocumentReader.processBeanDefinition() 完成 Bean 標籤解析的核心工作,如下:

	protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
		BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
		if (bdHolder != null) {
			bdHolder = delegate.decorateBeanDefinitionIfRequired
(ele, bdHolder); try { // 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); } // Send registration event. getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); } }

解析工作分為三步:1、解析預設標籤;2、解析預設標籤後下得自定義標籤;3、註冊解析後的 BeanDefinition。經過前面兩個步驟的解析,這時的 BeanDefinition 已經可以滿足後續的使用要求了,那麼接下來的工作就是將這些 BeanDefinition 進行註冊,也就是完成第三步。

註冊 BeanDefinition 由 BeanDefinitionReaderUtils.registerBeanDefinition() 完成。如下:

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

        // 註冊 beanName
        String beanName = definitionHolder.getBeanName();
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

        // 註冊 alias 
        String[] aliases = definitionHolder.getAliases();
        if (aliases != null) {
            for (String alias : aliases) {
                registry.registerAlias(beanName, alias);
            }
        }
    }

首先通過 beanName 註冊 BeanDefinition ,然後再註冊別名 alias。BeanDefinition 的註冊由介面 BeanDefinitionRegistry 定義。

通過 beanName 註冊

BeanDefinitionRegistry.registerBeanDefinition() 實現通過 beanName 註冊 BeanDefinition,如下:

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

        // 校驗 beanName 與 beanDefinition
        Assert.hasText(beanName, "Bean name must not be empty");
        Assert.notNull(beanDefinition, "BeanDefinition must not be null");

        if (beanDefinition instanceof AbstractBeanDefinition) {
            try {
                // 校驗 BeanDefinition
                // 這是註冊前的最後一次校驗了,主要是對屬性 methodOverrides 進行校驗
                ((AbstractBeanDefinition) beanDefinition).validate();
            }
            catch (BeanDefinitionValidationException ex) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                        "Validation of bean definition failed", ex);
            }
        }

        BeanDefinition oldBeanDefinition;

        // 從快取中獲取指定 beanName 的 BeanDefinition
        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 + "]");
                }
            }
            // 覆蓋 beanDefinition 與 被覆蓋的 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 + "]");
                }
            }

            // 允許覆蓋,直接覆蓋原有的 BeanDefinition
            this.beanDefinitionMap.put(beanName, beanDefinition);
        }
        /**
         * 不存在
         */
        else {

             // 檢測建立 Bean 階段是否已經開啟,如果開啟了則需要對 beanDefinitionMap 進行併發控制
            if (hasBeanCreationStarted()) {
                // beanDefinitionMap 為全域性變數,避免併發情況
                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 {
                // 不會存在併發情況,直接設定
                this.beanDefinitionMap.put(beanName, beanDefinition);
                this.beanDefinitionNames.add(beanName);
                this.manualSingletonNames.remove(beanName);
            }
            this.frozenBeanDefinitionNames = null;
        }

        if (oldBeanDefinition != null || containsSingleton(beanName)) {
            // 重新設定 beanName 對應的快取
            resetBeanDefinition(beanName);
        }
    }

處理過程如下:

  • 首先 BeanDefinition 進行校驗,該校驗也是註冊過程中的最後一次校驗了,主要是對 AbstractBeanDefinition 的 methodOverrides 屬性進行校驗
  • 根據 beanName 從快取中獲取 BeanDefinition,如果快取中存在,則根據 allowBeanDefinitionOverriding 標誌來判斷是否允許覆蓋,如果允許則直接覆蓋,否則丟擲 BeanDefinitionStoreException 異常
  • 若快取中沒有指定 beanName 的 BeanDefinition,則判斷當前階段是否已經開始了 Bean 的建立階段(),如果是,則需要對 beanDefinitionMap 進行加鎖控制併發問題,否則直接設定即可。對於 hasBeanCreationStarted() 方法後續做詳細介紹,這裡不過多闡述。
  • 若快取中存在該 beanName 或者 單利 bean 集合中存在該 beanName,則呼叫 resetBeanDefinition() 重置 BeanDefinition 快取。

其實整段程式碼的核心就在於 this.beanDefinitionMap.put(beanName, beanDefinition); 。BeanDefinition 的快取也不是神奇的東西,就是定義 map ,key 為 beanName,value 為 BeanDefinition。

註冊 alias

BeanDefinitionRegistry.registerAlias 完成 alias 的註冊。

    public void registerAlias(String name, String alias) {
        // 校驗 name 、 alias
        Assert.hasText(name, "'name' must not be empty");
        Assert.hasText(alias, "'alias' must not be empty");
        synchronized (this.aliasMap) {
            // name == alias 則去掉alias
            if (alias.equals(name)) {
                this.aliasMap.remove(alias);
            }
            else {
                // 快取快取記錄
                String registeredName = this.aliasMap.get(alias);
                if (registeredName != null) {
                    // 快取中的相等,則直接返回
                    if (registeredName.equals(name)) {
                        // An existing alias - no need to re-register
                        return;
                    }
                    // 不允許則丟擲異常
                    if (!allowAliasOverriding()) {
                        throw new IllegalStateException("Cannot register alias '" + alias + "' for name '" +
                                name + "': It is already registered for name '" + registeredName + "'.");
                    }
                }
                // 當 A --> B 存在時,如果再次出現 A --> B --> C 則丟擲異常
                checkForAliasCircle(name, alias);
                // 註冊 alias
                this.aliasMap.put(alias, name);
            }
        }
    }

註冊 alias 和註冊 BeanDefinition 的過程差不多。在最好呼叫了 checkForAliasCircle() 來對別名進行了檢測。

	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();
				return (registeredAlias.equals(alias) || hasAlias(registeredAlias, alias));
			}
		}
		return false;
	}

如果 name 、 alias 為 1 、3 ,則構成 (1,3),加入集合中存在(A,1)、(3,A)的情況則會出錯。

到這裡 BeanDefinition、alias 都已經注入到快取中,下一步則是等待初始化使用了。