spring源碼分析(三)BeanDefinition的注冊

上一篇博客介紹了spring如何解析配置文件根资,這篇文章會補充spring解析xml的細節(jié)及如何將這些對象封裝為BeanDefinition

DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions

上一篇文章差不多說到這個入口稻薇,這里繼續(xù)跟下去。

protected void doRegisterBeanDefinitions(Element root) {

    BeanDefinitionParserDelegate parent = this.delegate;
    this.delegate = createDelegate(getReaderContext(), root, parent);

    //如果是默認命名空間
    if (this.delegate.isDefaultNamespace(root)) {
        String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
        if (StringUtils.hasText(profileSpec)) {
            String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                    profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
            if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                if (logger.isInfoEnabled()) {
                    logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                            "] not matching: " + getReaderContext().getResource());
                }
                return;
            }
        }
    }
    
    //典型的模板方法模式
    preProcessXml(root);
    
    //實際解析beanDefinitions的地方
    parseBeanDefinitions(root, this.delegate);
    
    postProcessXml(root);
    this.delegate = parent;
}

解析bean標簽

1)將xml配置的bean的各種標簽解析出來桨仿,并封裝成一個GenericBeanDefinition對象
2)封裝到BeanDefinitionHolder中

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
    //id標簽
    String id = ele.getAttribute(ID_ATTRIBUTE);
    //name標簽
    String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

    List<String> aliases = new ArrayList<>();
    if (StringUtils.hasLength(nameAttr)) {
        String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
        aliases.addAll(Arrays.asList(nameArr));
    }

    // 如果沒有設置id標簽服傍,就使用alias(第0個)作為beanName
    String 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");
        }
    }

    //檢查是否有同名的bean
    if (containingBean == null) {
        checkNameUniqueness(beanName, aliases, ele);
    }

    //解析成GenericBeanDefinition
    AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
    if (beanDefinition != null) {
        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.isDebugEnabled()) {
                    logger.debug("Neither XML 'id' nor 'name' specified - " +
                            "using generated bean name [" + beanName + "]");
                }
            }
            catch (Exception ex) {
                error(ex.getMessage(), ele);
                return null;
            }
        }
        String[] aliasesArray = StringUtils.toStringArray(aliases);
        //最后封裝成一個BeanDefinitionHolder
        return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
    }

    return null;
}

其他屬性解析

上面步驟里有很關(guān)鍵的一步吹零,parseBeanDefinitionElement,這里對一些子屬性進行了解析套蒂。
比如:
scope,init-method,factory-method,destroy-method,depends-on

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

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

    //class標簽
    String className = null;
    if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
        className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
    }

    try {
        //parent標簽
        String parent = null;
        if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
            parent = ele.getAttribute(PARENT_ATTRIBUTE);
        }
        //最后返回的是 GenericBeanDefinition
        AbstractBeanDefinition bd = createBeanDefinition(className, parent);

        //解析其他屬性操刀,如scope,init-method,factory-method,destroy-method,depends-on 等
        parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
        bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

        //meta標簽
        parseMetaElements(ele, bd);
        //lookup-method
        parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
        //replaced-method
        parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

        //constructor-arg
        parseConstructorArgElements(ele, bd);
        //property
        parsePropertyElements(ele, bd);
        //qualifier
        parseQualifierElements(ele, bd);

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

        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ù)處理

到了這一步婴洼,xml配置已經(jīng)被spring轉(zhuǎn)換成BeanDefinitionHolder了,隨后的是后續(xù)處理欢唾。

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    //經(jīng)過這個方法后粉捻,bdHolder已經(jīng)包含我們配置文件中配置的各種屬性了,比如class杀迹、name押搪、id大州、alias
    BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
    if (bdHolder != null) {
        //解析自定義標簽,這一步可以替換解析出來的BeanDefinitionHolder
        bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
        try {
            // ******注冊******
            BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
        }
        catch (BeanDefinitionStoreException ex) {
            getReaderContext().error("Failed to register bean definition with name '" +
                    bdHolder.getBeanName() + "'", ele, ex);
        }
        // 發(fā)布一個注冊事件厦画,默認實現(xiàn)EmptyReaderEventListener對這個不進行處理
        getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
    }
}

BeanDefinition注冊

上一步驟根暑,很核心的一步,是對BeanDefinition的注冊排嫌,核心實現(xiàn)如下(委托給BeanDefinitionRegistry完成)

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

    // Register bean definition under primary name.
    String beanName = definitionHolder.getBeanName();
    registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

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

DefaultListableBeanFactory#registerBeanDefinition

追了下源碼淳地,默認實現(xiàn)在DefaultListableBeanFactory里帅容,實現(xiàn)邏輯如下:

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 {
            //methodOveride處理
            ((AbstractBeanDefinition) beanDefinition).validate();
        }
        catch (BeanDefinitionValidationException ex) {
            throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                    "Validation of bean definition failed", ex);
        }
    }

    BeanDefinition oldBeanDefinition;

    //查下之前是否有同樣的bean定義
    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.");
        }
        //... 各種注釋
        //存到緩存里
        this.beanDefinitionMap.put(beanName, beanDefinition);
    }
    else {
        //bean創(chuàng)建是否已啟動
        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);
                //注冊的beanDefinitionNames添加
                this.beanDefinitionNames = updatedDefinitions;
                if (this.manualSingletonNames.contains(beanName)) {
                    Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
                    updatedSingletons.remove(beanName);
                    this.manualSingletonNames = updatedSingletons;
                }
            }
        }
        else {
            // 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);
    }
}

總結(jié)下:
就是把BeanDefinition存到map里并徘、存到各種緩存里

總結(jié)

到這里扰魂,BeanDefinition的注冊就差不多清楚了。spring做了這幾件事:
1)將xml文件解析并封裝成對象GenericBeanDefinition
2)緩存下來beanName -> BeanDefinition

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末路幸,一起剝皮案震驚了整個濱河市付翁,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌砰识,老刑警劉巖佣渴,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異膨处,居然都是意外死亡砂竖,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進店門突硝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來置济,“玉大人,你說我怎么就攤上這事浙于。” “怎么了腐宋?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長围苫。 經(jīng)常有香客問我撤师,道長剂府,這世上最難降的妖魔是什么剃盾? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任痒谴,我火速辦了婚禮,結(jié)果婚禮上积蔚,老公的妹妹穿的比我還像新娘。我一直安慰自己怎顾,他們只是感情好漱贱,可當我...
    茶點故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著募强,像睡著了一般崇摄。 火紅的嫁衣襯著肌膚如雪擎值。 梳的紋絲不亂的頭發(fā)上配猫,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天泵肄,我揣著相機與錄音淑翼,去河邊找鬼。 笑死玄括,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的胃惜。 我是一名探鬼主播,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼船殉,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了挨厚?” 一聲冷哼從身側(cè)響起糠惫,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎巢价,沒想到半個月后固阁,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蹄溉,經(jīng)...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡柒爵,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年赚爵,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片唁奢。...
    茶點故事閱讀 40,680評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡窝剖,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出赐纱,到底是詐尸還是另有隱情,我是刑警寧澤诚隙,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布起胰,位于F島的核電站,受9級特大地震影響地消,放射性物質(zhì)發(fā)生泄漏炉峰。R本人自食惡果不足惜脉执,卻給世界環(huán)境...
    茶點故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一适瓦、第九天 我趴在偏房一處隱蔽的房頂上張望竿开。 院中可真熱鬧玻熙,春花似錦、人聲如沸列荔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽署恍。三九已至,卻和暖如春袁串,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背囱修。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工王悍, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人鲜漩。 一個月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓渠脉,卻偏偏與公主長得像,于是被迫代替她去往敵國和親芋膘。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,691評論 2 361