4.spring初級容器XmlBeanFactory初始化(三)

開篇

  1. 通過前一篇文章3.spring初級容器XmlBeanFactory初始化(二),我們已經(jīng)知道了spring是如何解析xml標簽,如:meta,lookup-method等苍柏,解析完成之后劣像,將會把解析好的標簽信息封裝到BeanDefinition中.
  2. 現(xiàn)在我們來看下,BeanDefinition是如何注冊到spring容器中的

BeanDefinition是從哪里開始注冊到spring容器中的

在前兩篇的文章中,我們重點看了spring如何解析bean標簽和屬性的,在解析完bean標簽和屬性之后,將解析的結(jié)果放到BeanDefinition中暑认,最后返回一個GenericBeanDefinition類型的BeanDefinition


    /**
     * Parse the bean definition itself, without regard to name or aliases. May return
     * {@code null} if problems occurred during the parsing of the bean definition.
     */
    @Nullable
    public AbstractBeanDefinition parseBeanDefinitionElement(
            Element ele, String beanName, @Nullable BeanDefinition containingBean) {

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

        String className = null;
        //1.如果標簽中存在class屬性,那么就獲取class屬性值
        if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
            className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
        }
        String parent = null;
        //2.如果標簽中存在parent標簽,那么就獲取parent標簽的屬性值
        if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
            parent = ele.getAttribute(PARENT_ATTRIBUTE);
        }

        try {
            //3.通過屬性class和parent,初步創(chuàng)建AbstractBeanDefinition
            AbstractBeanDefinition bd = createBeanDefinition(className, parent);

            //4.解析bean標簽中的各種屬性递递,并封裝到AbstractBeanDefinition中
            parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
            bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

            // 5.解析標簽中各種子標簽元素贰逾,并將解析結(jié)果封裝到AbstractBeanDefinition中

            // 解析bean標簽中的meta
            parseMetaElements(ele, bd);
            // 解析bean標簽中的lookup-method子標簽元素
            parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
            // 解析bean標簽中replace-method子標簽元素
            parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

            // 解析bean的子標簽constructor-arg
            parseConstructorArgElements(ele, bd);
            //解析bean的子標簽property
            parsePropertyElements(ele, bd);
            //解析bean的子標簽qualifier
            parseQualifierElements(ele, bd);

            bd.setResource(this.readerContext.getResource());
            bd.setSource(extractSource(ele));

            //6.返回bd
            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;
    }

返回之后,我們繼續(xù)看調(diào)用parseBeanDefinitionElement方法的地方

/**
     * Parses the supplied {@code <bean>} element. May return {@code null}
     * if there were errors during parse. Errors are reported to the
     * {@link org.springframework.beans.factory.parsing.ProblemReporter}.
     */
    @Nullable
    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
        //1.解析bean標簽中的id和name屬性
        String id = ele.getAttribute(ID_ATTRIBUTE);
        String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

        //2.將屬性name通過","或";"分隔符進行分割匾二,并將數(shù)據(jù)添加到aliases中
        List<String> aliases = new ArrayList<>();
        if (StringUtils.hasLength(nameAttr)) {
            String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
            aliases.addAll(Arrays.asList(nameArr));
        }

        // 3.如果屬性id為空皮璧,那么就取aliases集合中的第一個value的值,作為bean的名稱
        String beanName = id;
        if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
            beanName = aliases.remove(0);
            if (logger.isTraceEnabled()) {
                logger.trace("No XML 'id' specified - using '" + beanName +
                        "' as bean name and " + aliases + " as aliases");
            }
        }

        if (containingBean == null) {
            checkNameUniqueness(beanName, aliases, ele);
        }

        // 4. 開始解析bean標簽,并將解析結(jié)果封裝為AbstractBeanDefinition
        AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
        if (beanDefinition != null) {
            // beanName不為空,直接跳過
            if (!StringUtils.hasText(beanName)) {
                try {
                    if (containingBean != null) {
                        beanName = BeanDefinitionReaderUtils.generateBeanName(
                                beanDefinition, this.readerContext.getRegistry(), true);
                    }
                    else {
                        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.isTraceEnabled()) {
                        logger.trace("Neither XML 'id' nor 'name' specified - " +
                                "using generated bean name [" + beanName + "]");
                    }
                }
                catch (Exception ex) {
                    error(ex.getMessage(), ele);
                    return null;
                }
            }
            // 5. 將解析的beanDefinition,beanName和aliase送矩,創(chuàng)建一個BeanDefinitionHolder
            String[] aliasesArray = StringUtils.toStringArray(aliases);
            return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
        }

        return null;
    }

這里會將解析好的bean標簽的BeanDefinition連同aliasesArray一起封裝成BeanDefinitionHolder蒸其,BeanDefinitionHolder也可以理解成BeanDefinition的一個對象义屏。

  1. 當我們繼續(xù)向上去找調(diào)用源頭的時候,就會發(fā)現(xiàn)了澳叉,這里在解析完bean的屬性和標簽之后五督,將解析的屬性和標簽以BeanDefinitionHolder類型返回,其實就是一個BeanDefinition對象
  2. 然后執(zhí)行delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);瓶殃,這里可以忽略充包,因為點進去看下,基本上就是一些自定義標簽的校驗
  3. 接著執(zhí)行的就是我們的核心遥椿,把bean注如到spring容器當中BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
/**
     * Process the given bean element, parsing the bean definition
     * and registering it with the registry.
     */
    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        // 1.解析bean標簽元素
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if (bdHolder != null) {
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
            try {
                // Register the final decorated instance.
                // 將解析到的bean注冊到spring容器當中
                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));
        }
    }

接下來基矮,我們來看下淆储,bean是如何注入到spring容器中的

/**
     * Register the given bean definition with the given bean factory.
     * @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.
        //首先,拿到bean的名稱
        String beanName = definitionHolder.getBeanName();
        //注冊BeanDefinition到spring容器中
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

        // Register aliases for bean name, if any.
        //如果bean有別名家浇,那么就注冊bean的別名
        String[] aliases = definitionHolder.getAliases();
        if (aliases != null) {
            for (String alias : aliases) {
                registry.registerAlias(beanName, alias);
            }
        }
    }

繼續(xù)看下registerBeanDefinition

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

        //如果beanDefinition是AbstractBeanDefinition的實例
        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);
            }
        }

        //判斷beanDefinitionMap中是否已經(jīng)存在beanName對應(yīng)的BeanDefinition遏考,這里的beanDefinitionMap
        //就是spring容器
        BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
        if (existingDefinition != null) {
            //如果配置BeanDefinition不能被覆蓋,那么就直接報錯
            if (!isAllowBeanDefinitionOverriding()) {
                throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
            }
            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 + "]");
                }
            }
            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 + "]");
                }
            }
            this.beanDefinitionMap.put(beanName, beanDefinition);
        }
        else {
            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;
                    removeManualSingletonName(beanName);
                }
            }
            else {
                // Still in startup registration phase
                //直接將beanDefinition放入到beanDefinitionMap
                this.beanDefinitionMap.put(beanName, beanDefinition);
                //記錄beanName
                this.beanDefinitionNames.add(beanName);
                removeManualSingletonName(beanName);
            }
            this.frozenBeanDefinitionNames = null;
        }

        //如果當前需要注冊的bean的beanName蓝谨,已經(jīng)在spring容器中存在beanDefinition
        //或者已經(jīng)根據(jù)beanName創(chuàng)建出來對應(yīng)的bean的單例bean對象了
        if (existingDefinition != null || containsSingleton(beanName)) {
            //重新調(diào)整beanName對應(yīng)的緩存
            resetBeanDefinition(beanName);
        }
        else if (isConfigurationFrozen()) {
            clearByTypeCache();
        }
    }

1.首先要校驗beanDefinition中的methodOverrides屬性灌具,在方法validate中不允許methodOverrides屬性和factoryMethodName屬性同時設(shè)置

1.1 在解析lookup-method標簽和replaced-method標簽時,會將這兩個標簽需要覆蓋的方法名設(shè)置到MethodOverrides中
1.2 一旦MethodOverrides不為空譬巫,這就意味著Spring創(chuàng)建出來bean還要重新覆寫這些方法
1.3 而factoryMethodName屬性也就是工廠方法的名稱咖楣,通過工廠方法也可以創(chuàng)建一個bean出來,但是這相比于Spring默認的創(chuàng)建方式而言芦昔,算是一種不允許外界覆蓋bean中方法的創(chuàng)建方式了
1.4 也就是說要么我們通過工廠方法創(chuàng)建bean诱贿,要么就按Spring普通的方式來創(chuàng)建bean,兩者選其一

  1. 接下來咕缎,可以看到珠十,spring容器就是一個Map,key為beanName,value為BeanDefinition,beanDefinitionMap是成員變量凭豪,難免會有并發(fā)安全問題焙蹭,所以這里使用多線程安全的ConcurrentHashMap作為Spring的容器

看完beanDefinition是如何注冊到spring的容器之后,會繼續(xù)判斷是否有別名嫂伞,如果有別名的話孔厉,就注冊別名


檢查bean是否有別名
@Override
    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) {
            //如果別名alias和bean的名稱相同
            if (alias.equals(name)) {
                //就不記錄該別名,并且從別名map中刪除
                this.aliasMap.remove(alias);
                if (logger.isDebugEnabled()) {
                    logger.debug("Alias definition '" + alias + "' ignored since it points to same name");
                }
            }
            else {
                String registeredName = this.aliasMap.get(alias);
                if (registeredName != null) {
                    //如果最新的別名帖努,和從別名map中獲取的相同撰豺,那么就直接空操作,避免重復操作
                    if (registeredName.equals(name)) {
                        // An existing alias - no need to re-register
                        return;
                    }
                    //如果不允許表名覆蓋拼余,就報錯
                    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 + "'");
                    }
                }
                //檢查別名是否存在循環(huán)
                checkForAliasCircle(name, alias);
                this.aliasMap.put(alias, name);
                if (logger.isTraceEnabled()) {
                    logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'");
                }
            }
        }
    }

總結(jié)

spring初級容器初始化
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末污桦,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子匙监,更是在濱河造成了極大的恐慌凡橱,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件舅柜,死亡現(xiàn)場離奇詭異梭纹,居然都是意外死亡,警方通過查閱死者的電腦和手機致份,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進店門变抽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事绍载」钭冢” “怎么了?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵击儡,是天一觀的道長塔沃。 經(jīng)常有香客問我,道長阳谍,這世上最難降的妖魔是什么蛀柴? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮矫夯,結(jié)果婚禮上鸽疾,老公的妹妹穿的比我還像新娘。我一直安慰自己训貌,他們只是感情好制肮,可當我...
    茶點故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著递沪,像睡著了一般豺鼻。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上款慨,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天儒飒,我揣著相機與錄音,去河邊找鬼樱调。 笑死约素,一個胖子當著我的面吹牛届良,可吹牛的內(nèi)容都是我干的笆凌。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼士葫,長吁一口氣:“原來是場噩夢啊……” “哼乞而!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起慢显,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤爪模,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后荚藻,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體屋灌,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年应狱,在試婚紗的時候發(fā)現(xiàn)自己被綠了共郭。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖除嘹,靈堂內(nèi)的尸體忽然破棺而出写半,到底是詐尸還是另有隱情,我是刑警寧澤尉咕,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布叠蝇,位于F島的核電站,受9級特大地震影響年缎,放射性物質(zhì)發(fā)生泄漏悔捶。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一单芜、第九天 我趴在偏房一處隱蔽的房頂上張望炎功。 院中可真熱鬧,春花似錦缓溅、人聲如沸蛇损。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽淤齐。三九已至,卻和暖如春袜匿,著一層夾襖步出監(jiān)牢的瞬間更啄,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工居灯, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留祭务,地道東北人。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓怪嫌,卻偏偏與公主長得像义锥,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子岩灭,可洞房花燭夜當晚...
    茶點故事閱讀 44,843評論 2 354

推薦閱讀更多精彩內(nèi)容