Spring源碼解析 -- 讀取bean元數(shù)據(jù)

Spring源碼解析 -- 讀取bean元數(shù)據(jù)
spring源碼解析 -- 構(gòu)造bean
spring源碼解析 -- 注入屬性
spring源碼解析 -- Spring Context
Spring源碼解析 -- AOP原理(1)
Spring源碼解析 -- AOP原理(2)
Spring源碼解析 -- SpringMvc原理

源碼分析基于spring 4.3.x

本文通過閱讀源碼,分析spring如何讀取xml配置中的bean元數(shù)據(jù)。
關(guān)于閱讀源碼的思路,可參考 -- 如何閱讀java源碼

本文主要分析XmlBeanFactory,XmlBeanFactory是一個(gè)簡單的容器, 從XML文件中讀取配置元數(shù)據(jù)。

BeanFactory xmlBeanFactory = new XmlBeanFactory(new ClassPathResource("spring.xml"));

XmlBeanFactory已過時(shí),但好在簡單,便于分析源碼。

XmlBeanFactory#構(gòu)造函數(shù) -> XmlBeanDefinitionReader#loadBeanDefinitions
-> XmlBeanDefinitionReader#doLoadBeanDefinitions

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
        throws BeanDefinitionStoreException {
    try {
        Document doc = doLoadDocument(inputSource, resource);   // #1
        return registerBeanDefinitions(doc, resource);  // #2
    }
    ...
}

#1 加載xml到Document類(使用org.w3c.dom讀取xml文件)
#2 registerBeanDefinitions -> 解析xml數(shù)據(jù)

XmlBeanDefinitionReader#registerBeanDefinitions

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); // #1
    int countBefore = getRegistry().getBeanDefinitionCount();
    documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); // #2
    return getRegistry().getBeanDefinitionCount() - countBefore;
}

#1 構(gòu)建BeanDefinitionDocumentReader
#2
createReaderContext -> 構(gòu)建ReaderContext,ReaderContext中存放了xml的resource,XmlBeanDefinitionReader等信息,這些后面都要使用。
registerBeanDefinitions -> 使用BeanDefinitionDocumentReader解析xml數(shù)據(jù)

注意,ReaderContext中存放了XmlBeanDefinitionReader,而XmlBeanDefinitionReader中存放了bean注冊器BeanDefinitionRegistry(后面注冊BeanDefinition要使用)。這個(gè)注冊器就是XmlBeanFactory,XmlBeanFactory中構(gòu)建XmlBeanDefinitionReader時(shí)將this作為Registry參數(shù)
XmlBeanDefinitionReader#reader

private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);

XmlBeanFactory的父類DefaultListableBeanFactory實(shí)現(xiàn)了BeanDefinitionRegistry。

DefaultBeanDefinitionDocumentReader#registerBeanDefinitions -> DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions

protected void doRegisterBeanDefinitions(Element root) {
    BeanDefinitionParserDelegate parent = this.delegate;
    this.delegate = createDelegate(getReaderContext(), root, parent);

    if (this.delegate.isDefaultNamespace(root)) {   // #1
        String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);  // #2
        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);    // #3
    parseBeanDefinitions(root, this.delegate);  // #4
    postProcessXml(root);

    this.delegate = parent;
}

#1 判斷元素的命名空間是否為spring的beans空間,這個(gè)方法后面也有使用
#2 處理PROFILE元素屬性
#3 預(yù)處理,為子類提供的擴(kuò)展方法
#4 parseBeanDefinitions -> 解析beans元素

DefaultBeanDefinitionDocumentReader#parseBeanDefinitions

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
    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;
                if (delegate.isDefaultNamespace(ele)) { 
                    parseDefaultElement(ele, delegate); // #1
                }
                else {
                    delegate.parseCustomElement(ele);   // #2
                }
            }
        }
    }
    else {
        delegate.parseCustomElement(root);
    }
}

#1 對于beans命名空間的元素,使用默認(rèn)的方法進(jìn)行解析
#2 處理非beans命名空間的元素,使用對應(yīng)的解析器處理
用戶也可以自定義標(biāo)簽及標(biāo)簽解析器。

DefaultBeanDefinitionDocumentReader#parseDefaultElement

    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
        if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { // #1
            importBeanDefinitionResource(ele);
        }
        else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { // #2
            processAliasRegistration(ele);
        }
        else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {  // #3
            processBeanDefinition(ele, delegate);
        }
        else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {  // #4
            doRegisterBeanDefinitions(ele);
        }
    }

#1 解析import元素
#2 解析alias元素
#3 解析bean元素
#4 解析beans元素

主要看對bean元素的解析,DefaultBeanDefinitionDocumentReader#processBeanDefinition

    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);   // #1
        if (bdHolder != null) {
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);    // #2
            try {
                BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());   // #3
            }
            catch (BeanDefinitionStoreException ex) {
                getReaderContext().error("Failed to register bean definition with name '" +
                        bdHolder.getBeanName() + "'", ele, ex);
            }
            // Send registration event.
            getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));  // #4
        }
    }

#1 根據(jù)xmleizo,生成BeanDefinitionHolder
#2 應(yīng)用NamespaceHandler#decorate擴(kuò)展方法
#3 將BeanDefinition注冊到spring上下文
#4 發(fā)送注冊事件

這里出現(xiàn)了一個(gè)很重要的類BeanDefinition。

BeanDefinition是spring對bean元數(shù)據(jù)的抽象, 是配置文件中<bean>元素在spring容器中的內(nèi)部表示類,存儲(chǔ)了bean的元數(shù)據(jù), 如屬性PropertyValues, BeanClassName,Scope等。
BeanDefinitionHolder中持有BeanDefinition實(shí)例,以及beanName和aliases屬性。

構(gòu)造BeanDefinition

BeanDefinitionParserDelegat#parseBeanDefinitionElement有幾個(gè)重載方法,最終都調(diào)用如下方法

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

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

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

    try {
        String parent = null;
        if (ele.hasAttribute(PARENT_ATTRIBUTE)) {   
            parent = ele.getAttribute(PARENT_ATTRIBUTE);
        }
        AbstractBeanDefinition bd = createBeanDefinition(className, parent); // #1

        parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);   // #2
        bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));    

        parseMetaElements(ele, bd); // #3
        parseLookupOverrideSubElements(ele, bd.getMethodOverrides());   // #4
        parseReplacedMethodSubElements(ele, bd.getMethodOverrides());   // #5

        parseConstructorArgElements(ele, bd);   // #6
        parsePropertyElements(ele, bd); // #7
        parseQualifierElements(ele, bd);    // #8

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

        return bd;
    }
    ...
}

#1 使用className和parent創(chuàng)建BeanDefinition
#2 處理scope等元素屬性
#3 處理meta元素屬性
#4 處理lookup-method元素屬性
#5 處理replaced-method元素屬性
#6 處理constructor-arg構(gòu)造方法參數(shù)
#7 處理property元素
#8 處理qualifier元素

看一下如何處理property元素
BeanDefinitionParserDelegate#parsePropertyElements -> BeanDefinitionParserDelegate#parsePropertyElement

public void parsePropertyElement(Element ele, BeanDefinition bd) {
    String propertyName = ele.getAttribute(NAME_ATTRIBUTE); // #1
    if (!StringUtils.hasLength(propertyName)) {
        error("Tag 'property' must have a 'name' attribute", ele);
        return;
    }
    this.parseState.push(new PropertyEntry(propertyName));
    try {
        if (bd.getPropertyValues().contains(propertyName)) {
            error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
            return;
        }
        Object val = parsePropertyValue(ele, bd, propertyName); // #2
        PropertyValue pv = new PropertyValue(propertyName, val);    // #3
        parseMetaElements(ele, pv);
        pv.setSource(extractSource(ele));
        bd.getPropertyValues().addPropertyValue(pv);    // #4
    }
    finally {
        this.parseState.pop();
    }
}

#1 解析屬性名
#2 將xml配置的屬性值轉(zhuǎn)化為內(nèi)部表示類
#3 創(chuàng)建PropertyValue,PropertyValue中name存放屬性名,value存放了xml配置原始值。
#4 添加到BeanDefinition,BeanDefinition使用MutablePropertyValues存放所有的屬性信息。
xml配置原始值并不是屬性最終值,而是xml配置在spring中對應(yīng)的內(nèi)部表示類,如property元素的value屬性會(huì)表示為TypedStringValue(類似于BeanDefinition表示<bean>元素)。

BeanDefinitionParserDelegate#parsePropertyValue

public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) {
    ...

    boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
    boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
    if ((hasRefAttribute && hasValueAttribute) ||
            ((hasRefAttribute || hasValueAttribute) && subElement != null)) {
        error(elementName +
                " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
    }

    if (hasRefAttribute) {  // #1
        String refName = ele.getAttribute(REF_ATTRIBUTE);
        if (!StringUtils.hasText(refName)) {
            error(elementName + " contains empty 'ref' attribute", ele);
        }
        RuntimeBeanReference ref = new RuntimeBeanReference(refName);
        ref.setSource(extractSource(ele));
        return ref;
    }
    else if (hasValueAttribute) {   // #2
        TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
        valueHolder.setSource(extractSource(ele));
        return valueHolder;
    }
    else if (subElement != null) {  // #3
        return parsePropertySubElement(subElement, bd);
    }
    else {
        // Neither child element nor "ref" or "value" attribute found.
        error(elementName + " must specify a ref or value", ele);
        return null;
    }
}

#1 如果property元素存在ref屬性,解析為RuntimeBeanReference
#2 如果存在value屬性,解析為TypedStringValue
#3 如果存在子標(biāo)簽,就解析子標(biāo)簽。子標(biāo)簽和spring內(nèi)部表示類對應(yīng)如下
子標(biāo)簽為bean,解析為BeanDefinitionHolder
子標(biāo)簽為ref,解析為RuntimeBeanReference
子標(biāo)簽為idref,解析為RuntimeBeanNameReference
子標(biāo)簽為value,解析為TypedStringValue
子標(biāo)簽為null,解析為TypedStringValue
子標(biāo)簽為array,解析為ManagedArray
子標(biāo)簽為list,解析為ManagedList
子標(biāo)簽為set,解析為ManagedSet
子標(biāo)簽為map,解析為ManagedMap
子標(biāo)簽為props,解析為ManagedProperties

注冊

回到DefaultBeanDefinitionDocumentReader#processBeanDefinition方法#3步驟, 看一下如何將BeanDefinition注冊到spring上下文
BeanDefinitionReaderUtils#registerBeanDefinition

public static void registerBeanDefinition(
        BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
        throws BeanDefinitionStoreException {
    
    String beanName = definitionHolder.getBeanName();
    registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());    // #1

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

#1 使用beanName注冊bean
#2 注冊別名alias

注冊器registry從ReaderContext中獲取,實(shí)際就是DefaultListableBeanFactory(注意上文對ReaderContext的說明)。
DefaultListableBeanFactory#registerBeanDefinition

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

    oldBeanDefinition = this.beanDefinitionMap.get(beanName);
    if (oldBeanDefinition != null) {
        ...
    }
    else {
        if (hasBeanCreationStarted()) { // #1
            synchronized (this.beanDefinitionMap) { // #2
                this.beanDefinitionMap.put(beanName, beanDefinition);   // #3
                List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1);
                updatedDefinitions.addAll(this.beanDefinitionNames);
                updatedDefinitions.add(beanName);
                this.beanDefinitionNames = updatedDefinitions;  // #4
                if (this.manualSingletonNames.contains(beanName)) {
                    Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames);
                    updatedSingletons.remove(beanName);
                    this.manualSingletonNames = updatedSingletons;  // #5
                }
            }
        }
        else {  // #6
            this.beanDefinitionMap.put(beanName, beanDefinition);
            this.beanDefinitionNames.add(beanName);
            this.manualSingletonNames.remove(beanName);
        }
        this.frozenBeanDefinitionNames = null;
    }

    if (oldBeanDefinition != null || containsSingleton(beanName)) {
        resetBeanDefinition(beanName);
    }
}

#1 判斷spring是否進(jìn)入構(gòu)建bean的階段
#2 beanDefinitionNames/manualSingletonNames不是線程安全類,需要加鎖同步
#3 添加beanDefinition到DefaultListableBeanFactory#beanDefinitionMap
#4 通過寫時(shí)復(fù)制,添加beanName到DefaultListableBeanFactory#beanDefinitionNames
#5 通過寫時(shí)復(fù)制,刪除manualSingletonNames的DefaultListableBeanFactory#beanDefinitionNames
#6 還在注冊階段,不需要加鎖同步

最后, 看一下DefaultBeanDefinitionDocumentReader#parseBeanDefinitions方法#2步驟對非beans空間的xml標(biāo)簽的處理, BeanDefinitionParserDelegate#parseCustomElement

public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
    String namespaceUri = getNamespaceURI(ele);
    NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);  // #1
    if (handler == null) {
        error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
        return null;
    }
    return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));   // #2
}

#1 通過命名空間獲取特定的NamespaceHandler
#2 使用NamespaceHandler進(jìn)行處理

如果您覺得本文不錯(cuò),歡迎關(guān)注我的微信公眾號(hào),您的關(guān)注是我堅(jiān)持的動(dòng)力!


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容