BeanDefinition 在IOC的解析和注冊

概述

前面我們介紹了BeanDefinition的載入和解析的過程,將我們定義好的Bean資源文件載入并轉換成了Document對象,然后Document對象通過BeanDefinitionDocumentReader來解析,這些動作完成以后,用戶自定義的BeanDefinition信息已經在IOC容器內建立起了自己的數據結構,以及相應的數據表示,但這些數據還不能在IOC容器中直接使用,需要在IOC容器中對這些BeanDefinition進行注冊。這個注冊為IOC容器提供了更友好的使用方式。在DefaultListableBeanDactory中是使用一個Map對象載入并持有這些BeanDefinition的,代碼如下所示:

    /** Map of bean definition objects, keyed by bean name. */
    /**持有BeanDefinition的map容器**/
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
源碼解析

1.核心流程時序圖:

核心流程時序圖

從源碼實現的角度我們可以分析得到上圖的調用關系。我們可以跟蹤源碼具體看一下注冊實現,在DefaultListableBeanFactory中實現了BeanDefinitionRegistry的接口,這個接口的實現完成了BeanDefinition向容器注冊。注冊過程就是將解析得到的BeanDefinition設置到Map中去,但是如果遇到同名的BeanDefinition,進行處理的時候需要依據allowBeanDefinitionOverriding的配置來完成。下面我們就BeanDefinition的注冊邏輯展開分析。

2.源代碼分析

  • registerBeanDefinitions核心流程代碼解析:

    
    /**
     * 通過解析Document解析注冊BeanDefinition
     * Register the bean definitions contained in the given DOM document.
     * Called by {@code loadBeanDefinitions}.
     * <p>Creates a new instance of the parser class and invokes
     * {@code registerBeanDefinitions} on it.
     * @param doc the DOM document
     * @param resource the resource descriptor (for context information)
     * @return the number of bean definitions found
     * @throws BeanDefinitionStoreException in case of parsing errors
     * @see #loadBeanDefinitions
     * @see #setDocumentReaderClass
     * @see BeanDefinitionDocumentReader#registerBeanDefinitions
     */
    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        // 獲取BeanDefinition的Document解析器
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        // 獲取已經注冊的BeanDefinition的個數
        int countBefore = getRegistry().getBeanDefinitionCount();
        // 創建XmlRederContext,解析Document并注冊BeanDefinition
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        // 計算新注冊的BeanDefinition的數量
        return getRegistry().getBeanDefinitionCount() - countBefore;
    }
    
  • registerBeanDefinitions的處理邏輯解析:

    /**
     * This implementation parses bean definitions according to the "spring-beans" XSD (or DTD, historically).
     * document中element的解析并注冊BeanDefinition·
     * <p>Opens a DOM Document; then initializes the default settings
     * specified at the {@code <beans/>} level; then parses the contained bean definitions.
     */
    @Override
    public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
        this.readerContext = readerContext;
        doRegisterBeanDefinitions(doc.getDocumentElement());
    }
    
  • doRegisterBeanDefinitions處理邏輯解析

    
    /**
     * Register each bean definition within the given root {@code <beans/>} element.
     */
    @SuppressWarnings("deprecation")  // for Environment.acceptsProfiles(String...)
    protected void doRegisterBeanDefinitions(Element root) {
        // Any nested <beans> elements will cause recursion in this method. In
        // 從<beans />配置中注冊每一個bean,如果有嵌套的beans,那么遞歸執行這個方法。
        // order to propagate and preserve <beans> default-* attributes correctly,
        // keep track of the current (parent) delegate, which may be null. Create
        // the new (child) delegate with a reference to the parent for fallback purposes,
        // then ultimately reset this.delegate back to its original (parent) reference.
        // this behavior emulates a stack of delegates without actually necessitating one.
        // 在遞歸的時候,跟蹤父級delegate,新的遞歸調用引用上個方法的delegate
        BeanDefinitionParserDelegate parent = this.delegate;
        //  創建 BeanDefinitionParserDelegate 對象,并進行設置到 delegate
        this.delegate = createDelegate(getReaderContext(), root, parent);
        // 檢查 <beans /> 根標簽的命名空間是否為空,或者是 http://www.springframework.org/schema/beans【1】
        if (this.delegate.isDefaultNamespace(root)) {
            // 處理 profile 屬性??蓞⒁姟禨pring3自定義環境配置 <beans profile="">》 【2】
            String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
            if (StringUtils.hasText(profileSpec)) {
                String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                        profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
                // We cannot use Profiles.of(...) since profile expressions are not supported
                // in XML config. See SPR-12458 for details.
                // 判定環境參數是否滿足,無效則不注冊
                if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                                "] not matching: " + getReaderContext().getResource());
                    }
                    return;
                }
            }
        }
        // 解析前處理目前是空處理,可以繼承,由子類去實現
        preProcessXml(root);
        // 解析xml為BeanDefinition并向容器注冊生成的BeanDefinition【3】
        parseBeanDefinitions(root, this.delegate);
        // 解析后處理,目前為空執行,子類可繼承處理
        postProcessXml(root);
        // 設置為最初的BeanDefinitionParserDelegate
        this.delegate = parent;
    }
    

    【1】 檢查<beans /> 根標簽的命名空間是否為空,或者是 http://www.springframework.org/schema/beans
    【2】檢查beans標簽的是否指定profile環境注冊,若profile參數是不滿足條件,則不注冊
    【3】解析element(<beans />)下的<bean/>元素并注冊到BeanDefinition的map容器中。
    說明:createDelegate方法執行主要是創建代理,然后代理首先初始化一些默認的屬性,DocumentDefaultsDefinition是存儲默認配置的對象:default-lazy-init、default-merge、default-autowiredefault-dependency-check、default-autowire-candidatesdefault-init-method、default-destroy-method。

  • parseBeanDefinitions處理邏輯解析

    /**
     * Parse the elements at the root level in the document: "import", "alias", "bean".
     *
     * @param root the DOM root element of the document
     */
    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        // 如果該節點使用默認命名空間,執行默認解析【1】
        if (delegate.isDefaultNamespace(root)) {
            // root節點下的子節點【2】
            NodeList nl = root.getChildNodes();
            for (int i = 0; i < nl.getLength(); i++) {
                Node node = nl.item(i);
                if (node instanceof Element) {
                    Element ele = (Element) node;
                    // 如果該節點使用默認命名空間,執行默認解析【3】
                    if (delegate.isDefaultNamespace(ele)) {
                        parseDefaultElement(ele, delegate);
                    } else { // 如果該節點非默認命名空間,執行自定義解析【4】
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        } else {// 如果根節點非默認命名空間,執行自定義解析【5】
            delegate.parseCustomElement(root);
        }
    }
    

    【1】如果該節點使用默認命名空間,執行默認解析
    【2】root節點下的子節點
    【3】如果該節點使用默認命名空間,執行默認解析
    【4】如果該節點非默認命名空間,執行自定義解析
    【5】如果該節點非默認命名空間,執行自定義解析

  • parseDefaultElement處理邏輯解析

    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
        // import標簽
        if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
            importBeanDefinitionResource(ele);
            //  alias 解析
        } else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
            processAliasRegistration(ele);
            //bean 解析
        } else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
            processBeanDefinition(ele, delegate);
            // beans元素解析
        } else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
            //遞歸解析beans
            doRegisterBeanDefinitions(ele);
        }
    }
    
  • processBeanDefinition處理邏輯解析

    /**
     * Process the given bean element, parsing the bean definition and registering it with the registry.
     */
    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        // 委托BeanDefinition類的parseBeanDefinitionElement方法進行元素解析,返回Beandefinition 【1】
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if (bdHolder != null) {
            // 當返回的bdHolder 不為空的情況下,若存在默認標簽的子節點下再有自定義屬性,還需要再次對自定義標簽進行解析.【2】
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
            try {
                // Register the final decorated instance.
                // 最終執行注冊邏輯【3】
                BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
            } catch (BeanDefinitionStoreException ex) {
                getReaderContext().error("Failed to register bean definition with name '" +
                        bdHolder.getBeanName() + "'", ele, ex);
            }
            // Send registration event.
            // 下發注冊事件,通知相關的監聽器,這個bean已經加載完成【4】
            getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
        }
    }
    

    【1】委托BeanDefinition類的parseBeanDefinitionElement方法進行元素解析,返回Beandefinition ,此時的bdHolder實例已經包含了我們配置文件中的各種屬性了,例如 : class,name,id,alias
    【2】當返回的bdHolder 不為空的情況下,若存在默認標簽的子節點下再有自定義屬性,還需要再次對自定義標簽進行解析.
    【3】最終執行BeanDefinition注冊邏輯
    【4】下發注冊事件,通知相關的監聽器,這個bean已經加載完成

  • registryBeanDefinition的注冊邏輯分析

    從代碼上分析我們的registryBeanDefinition的最終執行是在是通過public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry),注冊的過程是在解析xml過程中完成注冊邏輯的。

    //---------------------------------------------------------------------
    // Implementation of BeanDefinitionRegistry interface
    //---------------------------------------------------------------------
    // 注冊BeanDefinition
    @Override
    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 {
               // 【1】 校驗BeanDefinition,這也是注冊前的最后一次校驗了,主要是對屬性 methodOverrides 進行校驗。
               ((AbstractBeanDefinition) beanDefinition).validate();
           } catch (BeanDefinitionValidationException ex) {
               throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                       "Validation of bean definition failed", ex);
           }
       }
       //【2】從緩存中獲取指定beanName的 BeanDefinition,主要 判斷bean name下是否已經注冊過BeanDefinition
       BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
       //【3】如果改BenDefinition已經注冊,如果不允許覆蓋的話,則拋出異常
       if (existingDefinition != null) {
           if (!isAllowBeanDefinitionOverriding()) {
               throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
           }
           // 當前要注冊的BeanDefinition的role 大于 已經注冊過的BeanDefinition 打印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 + "]");
               }
           }
           // 打印 trace日志信息
           else {
               if (logger.isTraceEnabled()) {
                   logger.trace("Overriding bean definition for bean '" + beanName +
                           "' with an equivalent definition: replacing [" + existingDefinition +
                           "] with [" + beanDefinition + "]");
               }
           }
           // 覆蓋已經注冊的bean信息
           this.beanDefinitionMap.put(beanName, beanDefinition);
       }
       // 【4】未注冊執行注冊邏輯
       else {
           // 判斷Bean的創建階段是否已經開啟,開啟的話需要對beanDefinitionMap進行線程保護
           if (hasBeanCreationStarted()) {
               // Cannot modify startup-time collection elements anymore (for stable iteration)
               // beanDefinitionMap為全局變量,加鎖保護,防止創建階段和注冊階段的并發問題
               synchronized (this.beanDefinitionMap) {
                   // 添加到BeanDefinition的map 容器中
                   this.beanDefinitionMap.put(beanName, beanDefinition);
                   // 添加BeanName到 beanDefinitionNames中去(beanDefinitionNames初始化限制了大小為256,所以變更的時候需要引入一個中間變量-主要是擴容問題)
                   List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
                   // 更新最新的beanDefinitionNames
                   updatedDefinitions.addAll(this.beanDefinitionNames);
                   updatedDefinitions.add(beanName);
                   this.beanDefinitionNames = updatedDefinitions;
                   // 從 manualSingletonNames 移除 beanName
                   removeManualSingletonName(beanName);
               }
           } else {
               // Still in startup registration phase
               // 添加到 BeanDefinition 到 beanDefinitionMap 中。
               this.beanDefinitionMap.put(beanName, beanDefinition);
               // 添加 beanName 到 beanDefinitionNames 中
               this.beanDefinitionNames.add(beanName);
               //  從 manualSingletonNames 移除 beanName
               removeManualSingletonName(beanName);
           }
           this.frozenBeanDefinitionNames = null;
       }
       // 【5】重新設置beanName對應的緩存
       if (existingDefinition != null || containsSingleton(beanName)) {
           resetBeanDefinition(beanName);
       }
    }
    
    

    【1】對BeanDefinition進行校驗,這也是注冊過程中最后一次校驗了,主要是針對AbstractBeanDefinitionmethodOverride屬性進行校驗。
    【2】根據beanName從緩存中獲取BeanDefinition。
    【3】如果緩存中已經存在,則根據allowBeanDefinitionOverriding標簽判斷是否允許覆蓋,如果不允許覆蓋,則拋出BeanDefinitionStoreException異常。
    【4】若緩存中沒有beanName 的BeanDefinition對象,則判斷當前階段是否已經開始了Bean的創建階段,如果是則對僅限并發保護,對BeanDefinitionMap進行加鎖并發控制。否則直接設置即可。
    【5】若緩存存在改beanName或者單例bean集合中存在該beanName,則調用#resetBeanDefinition(beanName)方法,充值BeanDefinition緩存。

    整個階段的核心流程其實就是對beanDefinitionMap的操作,只要核心在于this.beanDefinitionMap.put(beanName, beanDefinition)方法,而BeanDefinition的存儲其實就是定義了個map,key為beanName,value為BeanDefinition。

小結
BeanDefinition解析注冊詳細流程圖

微信公眾號
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,732評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,214評論 3 426
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,781評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,588評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,315評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,699評論 1 327
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,698評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,882評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,441評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,189評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,388評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,933評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,613評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,023評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,310評論 1 293
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,112評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,334評論 2 377

推薦閱讀更多精彩內容