spring源碼解析 2 ——解析 BeanDefinition

spring源碼解析 2 ——解析 BeanDefinition

書接上回。

上回說了xml配置文件的讀取催什,最終獲取到一個(gè)Document對(duì)象

源碼節(jié)點(diǎn)如下:

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)throws BeanDefinitionStoreException {
    try {
    //加載并解析XML搀矫,獲取到一個(gè)Document對(duì)象隅要,使用的是SAX炒瘟,相比于DOM,SAX是一種速度更快省骂,更有效的方法通惫。它逐行掃描文檔悔政,一邊掃描一邊解析迁沫。
    Document doc = doLoadDocument(inputSource, resource);
    //注冊(cè)BeanDefinition:
    return registerBeanDefinitions(doc, resource);
}

今天我們就來看下 registerBeanDefinitions(doc, resource) 方法的具體邏輯

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    //創(chuàng)建BeanDefinitionDocumentReader以用于實(shí)際從XML文檔中讀取bean定義。默認(rèn)實(shí)現(xiàn)是DefaultBeanDefinitionDocumentReader
    BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
    //獲取本次讀取前擂啥,注冊(cè)器(這里指的就是這個(gè)BeanFactory)中的beanDefinition的個(gè)數(shù)
    int countBefore = getRegistry().getBeanDefinitionCount();
    //進(jìn)行具體的注冊(cè)動(dòng)作
    documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    //本次讀取后注冊(cè)器中的beanDefinition中的beanDefinition的個(gè)數(shù)
    return getRegistry().getBeanDefinitionCount() - countBefore;
}

創(chuàng)建了一個(gè)BeanDefinitionDocumentReader, 然后就都交給它去做了鳍徽。這里有個(gè)地方值得注意一下资锰,就是這個(gè)createReaderContext(resource)方法。

public XmlReaderContext createReaderContext(Resource resource) {
    return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
            this.sourceExtractor, this, getNamespaceHandlerResolver());
}
這里的this是指XmlBeanDefinitionReader對(duì)象阶祭。
getNamespaceHandlerResolver()方法可以看一下  
    /**
     * 命名空間處理器解決器, 名字直譯過來有點(diǎn)拗口. 這東西是干什么的呢?
     * 首先搞清楚xml中的命名空間是什么, 其實(shí)就是xml文件開頭那些 xmlns, 如下:
     * <?xml version="1.0" encoding="UTF-8"?>
     * <beans xmlns="http://www.springframework.org/schema/beans"
     *     xmlns:context="http://www.springframework.org/schema/context"
     *     xmlns:tx="http://www.springframework.org/schema/tx"
     *     xmlns:p="http://www.springframework.org/schema/p"
     *     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     *     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
     *  >
     *  則在下面xml文件中使用到的時(shí)候, 可加上前綴來表示該標(biāo)簽名是來自那個(gè)命名空間的, 可以理解為java中的包的概念,這樣在同一個(gè)xml文件中, 假設(shè)標(biāo)簽屬性名稱一樣, 可根據(jù)其所屬命名空間來區(qū)別它們
     *  使用如下,這個(gè)是命令空間 xmlns:p="http://www.springframework.org/schema/p"
     *  <bean id="student2" class="com.qys.base.Student" p:age="19" p:id="2" p:name="小紅"></bean>
     *  對(duì)xml的命名空間的理解,大家可以自己網(wǎng)上查一下
     *
     *  所以NamespaceHandlerResolver的主要作用就是根據(jù)傳入的命名空間的uri來獲取對(duì)應(yīng)的處理器.
     *
     */
    protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
        return new DefaultNamespaceHandlerResolver(getResourceLoader().getClassLoader());
    }   
DefaultNamespaceHandlerResolver是干嘛的呢绷杜,看下面的代碼
    /**
     * Locate the {@link NamespaceHandler} for the supplied namespace URI
     * from the configured mappings.
     * @param namespaceUri the relevant namespace URI
     * @return the located {@link NamespaceHandler}, or {@code null} if none found
     * 根據(jù)傳入的命名空間的uri來獲取對(duì)應(yīng)的處理器
     */
    @Override
    public NamespaceHandler resolve(String namespaceUri) {
        //獲取處理器映射, 首次會(huì)去讀取META-INF/spring.handlers配置文件
        //主要,首次讀取配置文件的時(shí)候, 只是存了個(gè)字符串, 還不是真正的java對(duì)象
        Map<String, Object> handlerMappings = getHandlerMappings();
        Object handlerOrClassName = handlerMappings.get(namespaceUri);
        if (handlerOrClassName == null) {
            return null;
        }
        else if (handlerOrClassName instanceof NamespaceHandler) {
            return (NamespaceHandler) handlerOrClassName;
        }
        else {
            String className = (String) handlerOrClassName;
            try {
                //根據(jù)字符串(該字符串是個(gè)類的全限定類名), 反射創(chuàng)建處理器對(duì)象
                Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
                if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
                    throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
                            "] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
                }
                NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
                //調(diào)用處理器的初始化方法
                namespaceHandler.init();
                //將對(duì)象放入映射器中, 下次再使用到就無需再創(chuàng)建對(duì)象了
                handlerMappings.put(namespaceUri, namespaceHandler);
                return namespaceHandler;
            }
            catch (ClassNotFoundException ex) {
            ...
             ...   
        }
    }
 這東西再后面的解析自定義標(biāo)簽會(huì)用到,這里有個(gè)印象就好胖翰,就是會(huì)去讀取META-INF/spring.handlers配置文件   

讓我們接著看回documentReader.registerBeanDefinitions(doc, createReaderContext(resource))方法:

/**
 * This implementation parses bean definitions according to the "spring-beans" XSD
 * (or DTD, historically).
 * <p>Opens a DOM Document; then initializes the default settings
 * specified at the {@code <beans/>} level; then parses the contained bean definitions.
 *
 * 此實(shí)現(xiàn)根據(jù)“ spring-beans” XSD(或DTD接剩,歷史上)來解析bean定義。
 * 打開DOM文檔萨咳;然后初始化在<beans>級(jí)別指定的默認(rèn)設(shè)置懊缺;然后解析包含的bean定義。
 */
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
    this.readerContext = readerContext;
    logger.debug("Loading bean definitions");
    Element root = doc.getDocumentElement();
    doRegisterBeanDefinitions(root);
}

    protected void doRegisterBeanDefinitions(Element root) {
        /**
         * <beans>是可以嵌套的培他,任何嵌套的<beans>元素都將導(dǎo)致此方法中的遞歸(具體的遞歸在后面的parseBeanDefinitions方法中)
         * 為了保證<beans>的default-*的屬性可以正確傳播和保留
         * 創(chuàng)建一個(gè)新的(子)委托時(shí)鹃两,并帶有對(duì)父參考的引用以進(jìn)行回退,然后最終將this.delegate重置回其原始(父)參考舀凛。
         * 所謂的嵌套俊扳,如下:
         *      <beans default-init-method="init" default-destroy-method="destroy">
         *         <beans>
         *             <bean id="teacher" class="com.xxx.xx.Teacher">
         *                 <property name="id" value="xxx"/>
         *                 <property name="name" value="xxx"/>
         *                 <property name="subject" value="xxx"/>
         *             </bean>
         *         </beans>
         *     </beans>
         */
        BeanDefinitionParserDelegate parent = this.delegate;
        //創(chuàng)建委托類
        this.delegate = createDelegate(getReaderContext(), root, parent);

        //判斷該節(jié)點(diǎn)元素的命名空間是否是默認(rèn)的命名空間, 這里指的是 http://www.springframework.org/schema/beans 命名空間
        if (this.delegate.isDefaultNamespace(root)) {
            //獲取 profile屬性值
            String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
            if (StringUtils.hasText(profileSpec)) {
                //<beans profile="dev,sit"> 可以用, ; 空格 來定義多個(gè)profile
                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.
                //判斷環(huán)境中是否有這個(gè)profile,若沒有猛遍,則該<beans>下的bean就不去做解析
                //傳入的profiles中馋记,只要一個(gè)匹配就返回true
                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;
                }
            }
        }
        //擴(kuò)展點(diǎn),此處為空實(shí)現(xiàn)
        preProcessXml(root);
        //開始真正的解析xml元素
        parseBeanDefinitions(root, this.delegate);
        //擴(kuò)展點(diǎn),此處為空實(shí)現(xiàn)
        postProcessXml(root);

        this.delegate = parent;
    }

首次進(jìn)來BeanDefinitionParserDelegate parent這個(gè)是null的, 后面如果有<beans>標(biāo)簽出現(xiàn)的話懊烤,會(huì)進(jìn)行遞歸調(diào)用梯醒,到時(shí)候就不是null了。這里先看一下this.delegate = createDelegate(getReaderContext(), root, parent)方法腌紧。

protected BeanDefinitionParserDelegate createDelegate(
        XmlReaderContext readerContext, Element root, BeanDefinitionParserDelegate parentDelegate) {
    BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext);
    //初始化默認(rèn)值
    delegate.initDefaults(root, parentDelegate);
    return delegate;
}

public void initDefaults(Element root, BeanDefinitionParserDelegate parent) {
    //填充默認(rèn)值茸习,該方法中,會(huì)將父委托類的默認(rèn)屬性值傳遞個(gè)當(dāng)前委托類
    populateDefaults(this.defaults, (parent != null ? parent.defaults : null), root);
    //觸發(fā)默認(rèn)注冊(cè)事件: 一個(gè)拓展壁肋,運(yùn)行在默認(rèn)實(shí)行注冊(cè)后做一些特殊處理号胚,此處為一個(gè)空實(shí)現(xiàn)籽慢,什么也沒做
    this.readerContext.fireDefaultsRegistered(this.defaults);
}

populateDefaults(this.defaults, (parent != null ? parent.defaults : null), root)方法會(huì)去解析<beans>標(biāo)簽的各種默認(rèn)屬性。如:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
       default-init-method="init"
       default-lazy-init="true"
       default-autowire="byName"> 

這里我們就看一個(gè)就好:

/**
 *     default-init-method="init"   --- 默認(rèn)bean的初始化方法猫胁,優(yōu)先級(jí)低于<bean>的init-method
 *     default-destroy-method="destroy" --- 默認(rèn)bean的初始化方法箱亿,優(yōu)先級(jí)低于<bean>的destroy-method
 *                       若某個(gè)bean的Class中沒有init、destroy方法杜漠,則不會(huì)調(diào)用
 *     default-autowire --- 開啟bean的所有屬性自動(dòng)注入并且指定bean的屬性注入方式极景,優(yōu)先級(jí)低于<bean>的autowire
 *                       參考文檔 https://blog.csdn.net/otengyue/article/details/51509000
 *
 *     default-autowire-candidates -- 優(yōu)先級(jí)沒有低于<bean>的autowire-candidate
 *                       參考文檔 https://blog.csdn.net/Bof_jangle/article/details/50914075
 *                              https://blog.csdn.net/likun557/article/details/104438417
 *     default-merge="true" --- 子類無需再次定義父類定義過的屬性,可直接沿用,優(yōu)先級(jí)低于merge
*/           
    protected void populateDefaults(DocumentDefaultsDefinition defaults, DocumentDefaultsDefinition parentDefaults, Element root) {
        String lazyInit = root.getAttribute(DEFAULT_LAZY_INIT_ATTRIBUTE);
        //isDefaultValue方法, 判斷自身是否設(shè)置了該屬性,或者設(shè)置成默認(rèn)值. 若是,則使用父類的屬性
        if (isDefaultValue(lazyInit)) {
            // Potentially inherited from outer <beans> sections, otherwise falling back to false.
            // 可能從外部<beans>繼承驾茴,否則為false。
            lazyInit = (parentDefaults != null ? parentDefaults.getLazyInit() : FALSE_VALUE);
        }
        defaults.setLazyInit(lazyInit);
          ...
          ...
       }  

這里說的從外部<beans>繼承是什么意思呢氢卡?場(chǎng)景如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
       default-init-method="init"

            <beans>
            <!--若下面也設(shè)置為id="kakaxi1"的話, 則會(huì)覆蓋上面那個(gè)-->
            <bean id="kakaxi2" class="com.qys.base.Teacher">
                <property name="id" value="1"/>
                <property name="age" value="28"/>
                <property name="name" value="卡卡西2"/>
            </bean>
        </beans>   
     </beans>   

讓我們回到doRegisterBeanDefinitions(Element root)方法接著往下看锈至, 上面都有注釋了,直接看parseBeanDefinitions(root, this.delegate)方法:

    /**
     * Parse the elements at the root level in the document:
     * "import", "alias", "bean".
     * @param root the DOM root element of the document
     *  解析根元素的子元素, 即<beans>下的 <bean>译秦、<import>峡捡、<alias> 元素
     */
    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        //判斷一下該root元素是否為默認(rèn)的命名空間下, 即 http://www.springframework.org/schema/beans , 因?yàn)橛锌赡苁亲远x的標(biāo)簽
        if (delegate.isDefaultNamespace(root)) {
            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;
                    //判斷字標(biāo)簽是否在默認(rèn)命名空間下, 因?yàn)槠鋵?shí)<beans>下還可以有其他命名空間的標(biāo)簽, 比如:
                    // <aop:config>、<context:component-scan base-package="xxx"/>
                    if (delegate.isDefaultNamespace(ele)) {
                        //解析默認(rèn)標(biāo)簽(命名空間是http://www.springframework.org/schema/beans的)
                        parseDefaultElement(ele, delegate);
                    }
                    else {
                        //解析其他命名空間的標(biāo)簽
                        //<context:component-scan base-package="xxx"/>
                        //<tx:advice transaction-manager="xxx"/>
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        }
        else {
            //解析自定義標(biāo)簽
            delegate.parseCustomElement(root);
        }
    }

自定義標(biāo)簽咱們先不看筑悴,先看默認(rèn)命名空間下的標(biāo)簽们拙,進(jìn)入parseDefaultElement(ele, delegate)方法:

    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
        //處理<import>標(biāo)簽
        if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
            importBeanDefinitionResource(ele);
        }
        //處理<alias>標(biāo)簽
        else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
            processAliasRegistration(ele);
        }
        //處理<bean>標(biāo)簽
        else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
            processBeanDefinition(ele, delegate);
        }
        //處理<beans>元素, 因?yàn)?lt;beans>元素可以嵌套的
        else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
            //若是嵌套的<beans>標(biāo)簽,則遞歸處理
            doRegisterBeanDefinitions(ele);
        }
    }

這里咱們先看最復(fù)雜的<bean>標(biāo)簽的解析阁吝,進(jìn)入processBeanDefinition(ele, delegate)方法

    /**
     * Process the given bean element, parsing the bean definition
     * and registering it with the registry.
     * 解析<bean>標(biāo)簽,
     * 記住,這個(gè) delegate--包含--> XmlReaderContext --包含--> XmlBeanDefinitionReader --包含--> BeanDefinitionRegistry--XmlBeanFactory實(shí)現(xiàn)(或者其他工廠)
     * 也就是說, 這個(gè)元素解析完后,就可以注冊(cè)到工廠中了
     */
    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        //BeanDefinitionHolder 一個(gè)持有 解析后BeanDefinition砚婆、beanName、bean的aliases 的東西
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if (bdHolder != null) {
            //如果有必要的話,對(duì)BeanDefinition進(jìn)行裝飾
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
            try {
                // Register the final decorated instance.
                //getReaderContext().getRegistry() 返回的就是當(dāng)前 beanFactory
                //將最后得到的BeanDefinition進(jìn)行注冊(cè), 這樣,beanFactory就持有了beanDefinition
                BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
            }
            catch (BeanDefinitionStoreException ex) {
                getReaderContext().error("Failed to register bean definition with name '" +
                        bdHolder.getBeanName() + "'", ele, ex);
            }
            // Send registration event.
            //注冊(cè)完畢后, 發(fā)送注冊(cè)事件
            //這里是一個(gè)拓展點(diǎn),當(dāng)開發(fā)人員需要對(duì)注冊(cè)BeanDefinition事件進(jìn)行監(jiān)聽時(shí),可以通過注冊(cè)監(jiān)聽器的方式并將處理邏輯寫入監(jiān)聽器中.
            //目前spring中沒有對(duì)此事件做任何邏輯處理
            getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
        }
    }

這個(gè)方法:
1突勇、解析元素装盯,封裝成一個(gè)BeanDefinitionHolder對(duì)象。
2甲馋、注冊(cè)beanDefinition對(duì)象

先看如何解析封裝的:

    /**
     * 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}.
     * 解析<bean>元素埂奈,獲取BeanDefinition對(duì)象
     */
    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
        String id = ele.getAttribute(ID_ATTRIBUTE);
        //name屬性可以多個(gè), 用逗號(hào)、分號(hào)定躏、空格分隔
        String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

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

        String beanName = id;
        //如果 id屬性值為空且name屬性不為空, 則取name屬性的第一個(gè)值為beanName, 否則去id屬性值為beanName
        if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
            //將別名集合中的第一個(gè)別名移出集合, 并賦值給beanName
            beanName = aliases.remove(0);
            if (logger.isDebugEnabled()) {
                logger.debug("No XML 'id' specified - using '" + beanName +
                        "' as bean name and " + aliases + " as aliases");
            }
        }

        //若是嵌入bean則不會(huì)校驗(yàn)這個(gè)beanName的唯一性的,只有非嵌入才會(huì)校驗(yàn)
        if (containingBean == null) {
            //校驗(yàn)beanName在當(dāng)前標(biāo)簽層級(jí)層級(jí)的唯一性, 不能是別人已經(jīng)使用了的, 也不能是別名中已經(jīng)使用了的
            //如若嵌入的bean,其名字是允許重復(fù)的, 非嵌入不允許重復(fù)
            checkNameUniqueness(beanName, aliases, ele);
        }
        //解析<bean>標(biāo)簽里的其他屬性或者子標(biāo)簽, 返回一個(gè)BeanDefinition對(duì)象
        AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);

        if (beanDefinition != null) {
            if (!StringUtils.hasText(beanName)) {
                //處理id跟name屬性都沒設(shè)置值的情況
                try {
                    //如果beanName不存在, 那么根據(jù)spring中提供的命名規(guī)則為當(dāng)前bean生產(chǎn)對(duì)應(yīng)的beanName
                    //這里會(huì)區(qū)分是否為嵌入的bean, 即該<bean>是嵌入在另一個(gè)<bean>中的, 如:
                    //          <bean id="class1" class="com.qys.base.SchoolClass">
                    //              <property name="id" value="1"/>
                    //              <property name="grade" value="2"/>
                    //              <property name="classNo" value="1"/>
                    //              <property name="teacher">
                    //                  <bean class="com.qys.base.Teacher">
                    //                      <property name="id" value="1"/>
                    //                      <property name="age" value="20"/>
                    //                      <property name="name" value="伊魯卡"/>
                    //                  </bean>
                    //              </property>
                    //          </bean>
                    if (containingBean != null) {
                        //處理嵌入的bean
                        beanName = BeanDefinitionReaderUtils.generateBeanName(
                                beanDefinition, this.readerContext.getRegistry(), true);
                    }
                    else {
                        //處理非嵌入的bean, 調(diào)用DefaultBeanNameGenerator生成一個(gè)beanName
                        //如果安裝默認(rèn)的配置, 最后還是會(huì)走到
                        // BeanDefinitionReaderUtils.generateBeanName方法, 只是isInnerBean入?yún)⑹莊alse
                        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.
                        //如果生成器返回了類名加后綴账磺,那么如果仍然可能,為普通bean類名注冊(cè)一個(gè)別名痊远。
                        //這是Spring1.2/2.0向后兼容性的預(yù)期結(jié)果垮抗。
                        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);
            return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
        }

        return null;
    }

如果想看比較細(xì)節(jié)的話,可以看下checkNameUniqueness(beanName, aliases, ele)方法:

    /**
     * Validate that the specified bean name and aliases have not been used already
     * within the current level of beans element nesting.
     * 驗(yàn)證指定的bean名稱和別名是否尚未在當(dāng)前級(jí)別的bean元素嵌套中使用拗引。
     * 當(dāng)前級(jí)別的bean元素, 也就是說,如果是在 當(dāng)前不同級(jí)別的,就不會(huì)有問題.
     * 場(chǎng)景1:
     *      <bean id="kakaxi1" class="com.qys.base.Teacher">
     *          <property name="id" value="1"/>
     *          <property name="age" value="28"/>
     *          <property name="name" value="卡卡西1"/>
     *      </bean>
     *      <beans>
     *          <bean id="kakaxi1" class="com.qys.base.Teacher">
     *              <property name="id" value="1"/>
     *              <property name="age" value="28"/>
     *              <property name="name" value="卡卡西2"/>
     *          </bean>
     *      </beans>
     *  以上設(shè)置就不會(huì)報(bào)錯(cuò), 若將 <beans></beans>去掉, 則會(huì)報(bào)錯(cuò)
     *  以上的設(shè)置, 卡卡西2會(huì)覆蓋卡卡西1, 也就是說根據(jù)名字獲取bean的時(shí)候, 返回的是卡卡西2
     *  場(chǎng)景2:
     *  <bean id="kakaxi1" class="com.qys.base.Teacher">
     *          <property name="id" value="1"/>
     *          <property name="age" value="28"/>
     *          <property name="name" value="卡卡西1"/>
     *  </bean>
     *  <bean id="class1" class="com.qys.base.SchoolClass">
     *          <property name="id" value="1"/>
     *          <property name="grade" value="2"/>
     *          <property name="classNo" value="1"/>
     *          <property name="teacher">
     *              <bean id="kakaxi1" class="com.qys.base.Teacher">
     *                  <property name="id" value="1"/>
     *                  <property name="age" value="20"/>
     *                  <property name="name" value="卡卡西1"/>
     *              </bean>
     *          </property>
     *  </bean>
     *  以上配置也不會(huì)報(bào)錯(cuò)借宵,甚至?xí)崾镜诙€(gè)kakaxi1可以刪除
     */
    protected void checkNameUniqueness(String beanName, List<String> aliases, Element beanElement) {
        String foundName = null;

        if (StringUtils.hasText(beanName) && this.usedNames.contains(beanName)) {
            foundName = beanName;
        }
        if (foundName == null) {
            //若aliases中的任意一個(gè),在usedNames中找到了, 就返回賦值給foundName
            foundName = CollectionUtils.findFirstMatch(this.usedNames, aliases);
        }
        if (foundName != null) {
            error("Bean name '" + foundName + "' is already used in this <beans> element", beanElement);
        }

        this.usedNames.add(beanName);
        this.usedNames.addAll(aliases);
    }

解析<bean>標(biāo)簽的具體邏輯在parseBeanDefinitionElement(ele, beanName, containingBean)方法里。

    /**
     * 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.
     * 解析bean定義本身矾削,而不考慮名稱或別名.如果在分析bean定義期間出現(xiàn)問題可能會(huì)返回null
     */
    public AbstractBeanDefinition parseBeanDefinitionElement(
            Element ele, String beanName, BeanDefinition containingBean) {

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

        String className = null;
        //解析 class屬性
        if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
            className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
        }

        try {
            String parent = null;
            //解析 parent 屬性
            if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
                parent = ele.getAttribute(PARENT_ATTRIBUTE);
            }
            //創(chuàng)建BeanDefinition對(duì)象來封裝解析的屬性值,這里創(chuàng)建的是 GenericBeanDefinition
            AbstractBeanDefinition bd = createBeanDefinition(className, parent);

            //解析<bean>標(biāo)簽的屬性, 將其封裝至BeanDefinition對(duì)象中,如scope壤玫、abstract豁护、init-method等
            parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);

            //<bean>標(biāo)簽的description屬性, 用于生成文檔用的, 沒啥實(shí)際影響, 這里就不去管這個(gè)標(biāo)簽了
            bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

            //解析<bean>的子標(biāo)簽<meta>
            parseMetaElements(ele, bd);
            //解析<lookup-method>標(biāo)簽
            parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
            //解析<replaced-method>標(biāo)簽
            parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
            //解析<constructor-arg>標(biāo)簽
            parseConstructorArgElements(ele, bd);
            //解析<property>標(biāo)簽, 底層會(huì)走到跟解析<constructor-arg>標(biāo)簽一樣的方法
            parsePropertyElements(ele, bd);
            //解析<qualifier>標(biāo)簽, 平常我們都是使用@qualifier注解,在使用@Autowire自動(dòng)注入的時(shí)候,加上@Qualifier(“test”)可以指定注入哪個(gè)對(duì)象欲间;
            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;
    }

感覺內(nèi)容有點(diǎn)多楚里,放下一篇文章吧。

文字寫得比較少猎贴,因?yàn)槎荚诖a注釋里了班缎。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市她渴,隨后出現(xiàn)的幾起案子达址,更是在濱河造成了極大的恐慌盹靴,老刑警劉巖蜗巧,帶你破解...
    沈念sama閱讀 211,194評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異搞监,居然都是意外死亡苛败,警方通過查閱死者的電腦和手機(jī)满葛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來罢屈,“玉大人嘀韧,你說我怎么就攤上這事〔疲” “怎么了锄贷?”我有些...
    開封第一講書人閱讀 156,780評(píng)論 0 346
  • 文/不壞的土叔 我叫張陵,是天一觀的道長鄙币。 經(jīng)常有香客問我肃叶,道長,這世上最難降的妖魔是什么十嘿? 我笑而不...
    開封第一講書人閱讀 56,388評(píng)論 1 283
  • 正文 為了忘掉前任因惭,我火速辦了婚禮,結(jié)果婚禮上绩衷,老公的妹妹穿的比我還像新娘蹦魔。我一直安慰自己,他們只是感情好咳燕,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評(píng)論 5 384
  • 文/花漫 我一把揭開白布勿决。 她就那樣靜靜地躺著,像睡著了一般招盲。 火紅的嫁衣襯著肌膚如雪低缩。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,764評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音咆繁,去河邊找鬼讳推。 笑死,一個(gè)胖子當(dāng)著我的面吹牛玩般,可吹牛的內(nèi)容都是我干的银觅。 我是一名探鬼主播,決...
    沈念sama閱讀 38,907評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼坏为,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼究驴!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起匀伏,我...
    開封第一講書人閱讀 37,679評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤洒忧,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后帘撰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體跑慕,經(jīng)...
    沈念sama閱讀 44,122評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評(píng)論 2 325
  • 正文 我和宋清朗相戀三年摧找,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片牢硅。...
    茶點(diǎn)故事閱讀 38,605評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蹬耘,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出减余,到底是詐尸還是另有隱情综苔,我是刑警寧澤,帶...
    沈念sama閱讀 34,270評(píng)論 4 329
  • 正文 年R本政府宣布位岔,位于F島的核電站如筛,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏抒抬。R本人自食惡果不足惜杨刨,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望擦剑。 院中可真熱鬧妖胀,春花似錦、人聲如沸惠勒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽纠屋。三九已至涂臣,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間售担,已是汗流浹背赁遗。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評(píng)論 1 265
  • 我被黑心中介騙來泰國打工署辉, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人吼和。 一個(gè)月前我還...
    沈念sama閱讀 46,297評(píng)論 2 360
  • 正文 我出身青樓涨薪,卻偏偏與公主長得像,于是被迫代替她去往敵國和親炫乓。 傳聞我的和親對(duì)象是個(gè)殘疾皇子刚夺,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評(píng)論 2 348

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