3.spring初级容器XmlBeanFactory初始化(二)

开篇

  1. 承上启下,回顾上一篇文章内容,以及遗留问题
  2. spring如何加载xml中的各种标签以及如何获取标签中的属性值
  3. BeanDefinition如何注册到spring容器中(待更新)

一、简单回顾一下spring初级容器XmlBeanFactory

在之前的笔记中,spring初级容器XmlBeanFactory初始化,我们已经了解,spring初级容器XmlBeanFactory在初始化的时候

  1. 首先,在构造XmlBeanFactory容器时,会将applicationContext.xml配置文件封装成Resource,然后将Resouce资源作为XmlBeanFactory的构造方法参数,创建XmlBeanFactory
  2. 在XmlBeanFactory构造方法中,首先会添加忽略感知接口,然后将applicationContext.xml文件中的标签封装成Document对象,解析Document中的bean,然后将解析的bean的注入到spring容器中
  3. 今天接着分析,spring是如何解析applicationContext.xml中的标签,如何将解析的bean注入到spring容器中

二、spring如何解析xml文件中的各种标签和属性值

  1. 首先,接着上一篇文章spring初级容器XmlBeanFactory初始化的最后,doLoadBeanDefinitions(InputSource inputSource, Resource resource)方法
/**
     * Actually load bean definitions from the specified XML file.
     * @param inputSource the SAX InputSource to read from
     * @param resource the resource descriptor for the XML file
     * @return the number of bean definitions found
     * @throws BeanDefinitionStoreException in case of loading or parsing errors
     * @see #doLoadDocument
     * @see #registerBeanDefinitions
     */
    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {

        try {
            //将传进来的inputSource和resource,封装成Document对象
            Document doc = doLoadDocument(inputSource, resource);
            //解析document对象,并将解析的bean注入到spring中
            int count = registerBeanDefinitions(doc, resource);
            if (logger.isDebugEnabled()) {
                logger.debug("Loaded " + count + " bean definitions from " + resource);
            }
            return count;
        }
        catch (BeanDefinitionStoreException ex) {
            throw ex;
        }
        catch (SAXParseException ex) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(),
                    "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
        }
        catch (SAXException ex) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(),
                    "XML document from " + resource + " is invalid", ex);
        }
        catch (ParserConfigurationException ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(),
                    "Parser configuration exception parsing XML from " + resource, ex);
        }
        catch (IOException ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(),
                    "IOException parsing XML document from " + resource, ex);
        }
        catch (Throwable ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(),
                    "Unexpected exception parsing XML document from " + resource, ex);
        }
    }

可以看到,会将封装好的inputSource和资源Resurce作为参数,传递到方法doLoadDocument(inputSource, resource);方法中,其实就是解析资源,然后封装成Document对象

/**
     * Actually load the specified document using the configured DocumentLoader.
     * @param inputSource the SAX InputSource to read from
     * @param resource the resource descriptor for the XML file
     * @return the DOM Document
     * @throws Exception when thrown from the DocumentLoader
     * @see #setDocumentLoader
     * @see DocumentLoader#loadDocument
     */
    protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
        return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
                getValidationModeForResource(resource), isNamespaceAware());
    }

这里可以看到,将在资源的组件由原来的XmlBeanDefinitionReader递交给DocumentLoader,该组件组要是用来加载Document的,如下


DefaultCocumentLoader

继续看下loadDocument

/**
     * Load the {@link Document} at the supplied {@link InputSource} using the standard JAXP-configured
     * XML parser.
     */
    @Override
    public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
            ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {

        DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
        if (logger.isTraceEnabled()) {
            logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]");
        }
        DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
        return builder.parse(inputSource);
    }

可以看到,spring通过Doc来解析xml文件,那spring是如何加载xml文件的呢?我们先回到上面的doLoadDocument方法中

entityResolver解析器

在方法loadDocument方法中,传递进来的EntityResolver其实就是在这里通过getEntityResolver()方法获取的,我们来看下getEntityResolver方法

/**
     * Return the EntityResolver to use, building a default resolver
     * if none specified.
     */
    protected EntityResolver getEntityResolver() {
        if (this.entityResolver == null) {
            // Determine default EntityResolver to use.
            ResourceLoader resourceLoader = getResourceLoader();
            if (resourceLoader != null) {
                this.entityResolver = new ResourceEntityResolver(resourceLoader);
            }
            else {
                this.entityResolver = new DelegatingEntityResolver(getBeanClassLoader());
            }
        }
        return this.entityResolver;
    }

可以看到,具体是使用ResourceEntityResolver还是DelegatingEntityResolver取决于getResourceLoader()方法返回的是否为非空

@Override
    @Nullable
    public ResourceLoader getResourceLoader() {
        return this.resourceLoader;
    }

看到这里,我们就需要继续跟踪一下,resourceLoader是在哪里进行初始化的,在AbstractBeanDefinitionReader构造方法中,如下

/**
     * Create a new AbstractBeanDefinitionReader for the given bean factory.
     * 

If the passed-in bean factory does not only implement the BeanDefinitionRegistry * interface but also the ResourceLoader interface, it will be used as default * ResourceLoader as well. This will usually be the case for * {@link org.springframework.context.ApplicationContext} implementations. *

If given a plain BeanDefinitionRegistry, the default ResourceLoader will be a * {@link org.springframework.core.io.support.PathMatchingResourcePatternResolver}. *

If the passed-in bean factory also implements {@link EnvironmentCapable} its * environment will be used by this reader. Otherwise, the reader will initialize and * use a {@link StandardEnvironment}. All ApplicationContext implementations are * EnvironmentCapable, while normal BeanFactory implementations are not. * @param registry the BeanFactory to load bean definitions into, * in the form of a BeanDefinitionRegistry * @see #setResourceLoader * @see #setEnvironment */ protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); this.registry = registry; // Determine ResourceLoader to use. if (this.registry instanceof ResourceLoader) { this.resourceLoader = (ResourceLoader) this.registry; } else { this.resourceLoader = new PathMatchingResourcePatternResolver(); } // Inherit Environment if possible if (this.registry instanceof EnvironmentCapable) { this.environment = ((EnvironmentCapable) this.registry).getEnvironment(); } else { this.environment = new StandardEnvironment(); } }

这里,我们需要知道registry的类型来确认resourceLoader的类型,通过类继承关系可以知道,AbstractBeanDefinitionReader是XmlBeanDefinitionReader的父类,而XmlBeanDefinitionReader是在哪里进行初始化的,还记得吗?


XmlBeanFactory中的XmlBeanDefinitionReader初始化

我们回到XmlBeanFactory类中,我们通过XmlBeanFactory类中的XmlBeanDefinitionReader的构造方法中,可以一步步的走到父类AbstractBeanDefinitionReader的构造方法中。


image.png

image.png

image.png

DTD和XSD

在spring的xml配置文件的开头中


applicationContext.xml
  1. XSD翻译成英文就是XML Schemas Definition,也就是XML模式的定义,通过XSD的声明文件可以约束我们在xml文件中不能随便乱写,以保证xml文件格式的正确性。
  2. 除了XSD之外,Spring还支持另外一种约束语言,也就是DTD,DTD翻译英文就是Document Type Definition,也就是文档类型的定义。
    在xml文件中,都可以通过 xsi:schemaLocation中的网址进行查看,具体的网址和规范

spring 如何解析DTD和XSD

我们现在回到上面的getEntityResolver()方法中

/**
     * Return the EntityResolver to use, building a default resolver
     * if none specified.
     */
    protected EntityResolver getEntityResolver() {
        if (this.entityResolver == null) {
            // Determine default EntityResolver to use.
            ResourceLoader resourceLoader = getResourceLoader();
            if (resourceLoader != null) {
                this.entityResolver = new ResourceEntityResolver(resourceLoader);
            }
            else {
                this.entityResolver = new DelegatingEntityResolver(getBeanClassLoader());
            }
        }
        return this.entityResolver;
    }

因为resourceLoader不为空,所以,entityResolver为:ResourceEntityResolver,我们接着看下ResourceEntityResolver构造方法中,是如何构造ResourceEntityResolver的。


DelegatingEntityResolver.png

BeansDtdResolver就是用来获取DTD声明文件的解析器,而PluggableSchemaResolver是用来获取XSD声明文件的解析器
我们先到BeansDtdResolver中看下,如何解析dtd文件的


BeansDtdResolver解析

我们可以看到,resolverEntity就是解析DTD文件的方法
该方法中有两个参数,分别是publicId和systemId,这两个参数其实就是我们在applicationContext.xml文件中的配置


dtd

通过ClassPathResourc类,从claspath下路径下加载spring-beans.dtd文件,并且将publicid和systemid封装到InputSource中
在创建ClassPathResource类时,将getClass(),也就是BeansDtdResolver传入进来了,这样的话,会在BeansDtdResolver所在classpath寻找spring-beans.dtd,我们再BeansDtdResolver所在的classpath去看下,如下:


spring-beans.dtd

DTD解析完成后,我们再来看下XSD如何解析,我们先到PluggableSchemaResolver中的resolveEntity方法中看下如何解析的


PluggableSchemaResolver
  1. systemId肯定为非空,此时调用getSchemaMappings()方法,通过systemId获取资源resourceLocation的位置
    那么,我们现在去getSchemaMappings方法中看下
getScheaMappings方法
  1. 刚开始,成员变量schemaMappings肯定为空
  2. 然后通过PropertiesLoaderUtils.loadAllProperties方法加载schemaMappingsLocation中的所有属性
  3. schemaMappingsLocation具体信息如下:
schemaMappingsLocation
  1. 默认情况下,在PluggableSchemaResolver构造方法中,已经将"META-INF/spring.schemas"赋值给变量schemaMappingsLocation
  2. 那么,我们就去spring-beans模块下,找到META-INF/spring.schemas
spring.schemas
  1. 由此可见,在spring.schemas文件中,存放的key就是systemId,存放的value为XSD声明文件在项目中的路径
  2. 我们再回到getSchemaMappings方法中,再回过头来看下,就很简单了,其实就是将spring.schemas文件中的所有封装成一个Map返回回去,然后通过systemId找到XSD文件在项目中路径去获取XSD文件

到此,DTD和XSD声明文件都是通过EntityResolver响应的实现类,已经完成,但是,离我们实际的如何校验、解析xml文件中的bean还很远,但是别急,我们先把校验和解析xml分析万,再来看看

我们再回到doLoadDocument方法中


doLoadDocument

这里可以看到,loadDocument方法,不仅传入了刚刚我们分析的getEntityResolver()方法返回的声明文件解析器,还传入了getValidationModeForResource,获取当前xml文件的校验类型

/**
     * Determine the validation mode for the specified {@link Resource}.
     * If no explicit validation mode has been configured, then the validation
     * mode gets {@link #detectValidationMode detected} from the given resource.
     * 

Override this method if you would like full control over the validation * mode, even when something other than {@link #VALIDATION_AUTO} was set. * @see #detectValidationMode */ protected int getValidationModeForResource(Resource resource) { //1.默认获取校验类型为VALIDATION_AUTO int validationModeToUse = getValidationMode(); if (validationModeToUse != VALIDATION_AUTO) { return validationModeToUse; } //2.自动检测校验模式 int detectedMode = detectValidationMode(resource); if (detectedMode != VALIDATION_AUTO) { return detectedMode; } // Hmm, we didn't get a clear indication... Let's assume XSD, // since apparently no DTD declaration has been found up until // detection stopped (before finding the document's root tag). return VALIDATION_XSD; }

默认校验类型如下


image.png

image.png

可以看到,第一个if条件不成立,那么我们到detectValidationMode自动检测中看下

/**
     * Detect the validation mode for the XML document in the supplied {@link InputStream}.
     * Note that the supplied {@link InputStream} is closed by this method before returning.
     * @param inputStream the InputStream to parse
     * @throws IOException in case of I/O failure
     * @see #VALIDATION_DTD
     * @see #VALIDATION_XSD
     */
    public int detectValidationMode(InputStream inputStream) throws IOException {
        // Peek into the file to look for DOCTYPE.
        //1.将输入流inputStream包装成一个缓冲字符输入流,方便读取InputStream
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        try {
            boolean isDtdValidated = false;
            String content;
            while ((content = reader.readLine()) != null) {
                content = consumeCommentTokens(content);
                if (this.inComment || !StringUtils.hasText(content)) {
                    continue;
                }
                //2.内容当中,是否包含"DOCTYPE",什么意思呢,也就是说,如果检测到xml文件中有DOCTYPE字符串
                // 就认为它是DTD文件,否则就是XSD文件
                if (hasDoctype(content)) {
                    isDtdValidated = true;
                    break;
                }
                if (hasOpeningTag(content)) {
                    // End of meaningful data...
                    break;
                }
            }
            return (isDtdValidated ? VALIDATION_DTD : VALIDATION_XSD);
        }
        catch (CharConversionException ex) {
            // Choked on some character encoding...
            // Leave the decision up to the caller.
            return VALIDATION_AUTO;
        }
        finally {
            reader.close();
        }
    }

这样,spring就可以根据具体的解析类型,分别使用不同的解析器去获取响应的校验文件,这样xml文件在解析时,至少对xml文件的基本格式和规范做了一定的保障
我们还看到,XSD和DTD对应的解析器EntityResolver,根据不同的类型,在jar包中加载对应的声明文件,这样的好处就是防止网络问题,在网上下载声明文件时,因为网络问题而对项目产生影响

加载标签

  1. 我们再次回到doLoadBeanDefinition的源码位置
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {

        try {
            //将传进来的inputSource和resource,封装成Document对象
            Document doc = doLoadDocument(inputSource, resource);
            //解析document对象,并将解析的bean注入到spring中
            int count = registerBeanDefinitions(doc, resource);
            if (logger.isDebugEnabled()) {
                logger.debug("Loaded " + count + " bean definitions from " + resource);
            }
            return count;
        }
        // 省略部分代码

在校验完xml文件之后,紧接着调用registerBeanDefinitions方法进行xml文件的解析和注册。

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        //1.通过反射,创建对象BeanDefinitionDocumentReader
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        //2.获取spring容器中,已经注册的bean的数量
        int countBefore = getRegistry().getBeanDefinitionCount();
        //3.通过documentReader解析Document,并将解析的bean注入到spring容器中
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        //4.获取本次注册spring容器中的数量,spring容器中的bean总数量-本次注册前spring容器中bean数量
        return getRegistry().getBeanDefinitionCount() - countBefore;
    }

我们到createBeanDefinitionDocumentReader中看下

protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
        return BeanUtils.instantiateClass(this.documentReaderClass);
    }

我们再到documentReaderClass中看下,documentReaderClass是BeanDefinitionDocumentReader


documentReaderClass

创建玩BeanDefinitionDocumentReader对象之后,开始计算当前spring容器中bean的数量,方便我们完成本次注册之后,统计本次注册bean的数量
然后再通过documentReader.registerBeanDefinitions进行解析Document

    /**
     * This implementation parses bean definitions according to the "spring-beans" XSD
     * (or DTD, historically).
     * 

Opens a DOM Document; then initializes the default settings * specified at the {@code } level; then parses the contained bean definitions. */ @Override public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; doRegisterBeanDefinitions(doc.getDocumentElement()); }

这里通过doc.getDocumentElement()读取文档中的元素,获取applicationContext.xml文件中整个根标签,将其传入到doRegisterBeanDefinitions方法中

我们到doRegisterBeanDefinitions方法中

/**
     * Register each bean definition within the given root {@code } element.
     */
    @SuppressWarnings("deprecation")  // for Environment.acceptsProfiles(String...)
    protected void doRegisterBeanDefinitions(Element root) {
        // Any nested  elements will cause recursion in this method. In
        // order to propagate and preserve  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.
        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);
                // 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);
        parseBeanDefinitions(root, this.delegate);
        postProcessXml(root);

        this.delegate = parent;
    }

protected BeanDefinitionParserDelegate createDelegate(
            XmlReaderContext readerContext, Element root, @Nullable BeanDefinitionParserDelegate parentDelegate) {

        BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext);
        delegate.initDefaults(root, parentDelegate);
        return delegate;
    }

BeanDefinitionParserDelegate就是Document封装BeanDefinition的一个代理类
我们到preProcessXml(root);parseBeanDefinitions(root, this.delegate);postProcessXml(root);这三个方法中看下

protected void preProcessXml(Element root) {
    }


/**
     * 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) {
        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);
                    }
                    else {
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        }
        else {
            delegate.parseCustomElement(root);
        }
    }

protected void postProcessXml(Element root) {
    }
  1. preProcessXml和postProcessXml都是空实现,因此,我们可以猜测,这两个方法是留给DefaultBeanDefinitionDocumentReader的子类去扩展的
  2. 所以,解析xml的逻辑就在parseBeanDefinitions方法中

可以看到,解析标签的工作完全交给了BeanDefinitionParserDelegate

    /**
     * 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)) {
            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);
                    }
                    else {
                        //解析自定义标签中的元素
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        }
        else {
            //解析自定义标签中的元素
            delegate.parseCustomElement(root);
        }
    }

这里首先是判断当前标签是不是默认标签,


判断是否是默认标签

这里判断是否是默认标签还是比较简单,如果namespaceUri为空或者namespaceUri为http://www.springframework.org/schema/beans
否则的话,就为自定义标签,其实,spring中有很多自己自定义的标签,如:,context:component-scan />

那么spring是如何解析默认标签的呢,我们继续到parseDefaultElement中

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

我们首先来看下DefaultBeanDefinitionDocumentReader中的几个常量


DefaultBeanDefinitionDocumentReader中的常量

BeanDefinitionParserDelegate中的常量


BeanDefinitionParserDelegate中的常量

可以看到,这些就是标签的名称,比如标签bean、alias、import、beans。
我们重点来分析下bean标签,因为这个标签也是我们最常用的标签,没有之一
/**
     * 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));
        }
    }

我们可以看到,这里还是委托BeanDefinitionParserDelegate来帮我们解析,直接通过参数传递的形式传递进来

下面我们来看下,BeanDefinitionParserDelegate是如何解析bean标签的

/**
     * Parses the supplied {@code } 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) {
        return parseBeanDefinitionElement(ele, null);
    }

/**
     * Parses the supplied {@code } 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通过","或";"分隔符进行分割,并将数据添加到aliases中
        List 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标签,并将解析结果封装为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,创建一个BeanDefinitionHolder
            String[] aliasesArray = StringUtils.toStringArray(aliases);
            return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
        }

        return null;
    }

这里的MULTI_VALUE_ATTRIBUTE_DELIMITERS的值为 ",; ",也就是说在配置属性name的值时,可以通过“,”或“;”作为分隔符,配置多个name属性值。如"order,orders",分割之后可以得到["order","orders"]的数组,放到aliases中
在第四步中,parseBeanDefinitionElement(ele, beanName, containingBean);极为关键,解析bean标签并封装成AbstractBeanDefinition

我们来看下解析bean标签并封装成AbstractBeanDefinition

可以看到,还是继续解析bean标签中的属性,分别获取属性class的值className,及属性parent的值parent,然后将这个两个属性的值传入方法createBeanDefinition中,构建一个AbstractBeanDefinition类型的对象bd。

/**
     * 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,初步创建AbstractBeanDefinition
            AbstractBeanDefinition bd = createBeanDefinition(className, parent);

            //4.解析bean标签中的各种属性,并封装到AbstractBeanDefinition中
            parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
            bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

            // 5.解析标签中各种子标签元素,并将解析结果封装到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;
    }

在上面的parseBeanDefinitionElement方法的第三步中,创建BeanDefinition,我们来看下

/**
     * Create a bean definition for the given class name and parent name.
     * @param className the name of the bean class
     * @param parentName the name of the bean's parent bean
     * @return the newly created bean definition
     * @throws ClassNotFoundException if bean class resolution was attempted but failed
     */
    protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName)
            throws ClassNotFoundException {

        return BeanDefinitionReaderUtils.createBeanDefinition(
                parentName, className, this.readerContext.getBeanClassLoader());
    }

/**
     * Create a new GenericBeanDefinition for the given parent name and class name,
     * eagerly loading the bean class if a ClassLoader has been specified.
     * @param parentName the name of the parent bean, if any
     * @param className the name of the bean class, if any
     * @param classLoader the ClassLoader to use for loading bean classes
     * (can be {@code null} to just register bean classes by name)
     * @return the bean definition
     * @throws ClassNotFoundException if the bean class could not be loaded
     */
    public static AbstractBeanDefinition createBeanDefinition(
            @Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {

        GenericBeanDefinition bd = new GenericBeanDefinition();
        bd.setParentName(parentName);
        if (className != null) {
            if (classLoader != null) {
                bd.setBeanClass(ClassUtils.forName(className, classLoader));
            }
            else {
                bd.setBeanClassName(className);
            }
        }
        return bd;
    }

可以看到,实际上是创建的BeanDefinition为GenericBeanDefinition,并且将parent和class属性的值都设置到GenericBeanDefinition之后并返回。
到此,我们可以知道,bean在spring容器中都是以BeanDefinition的形式存在,而BeanDefinition只是一个接口,因此。Spring在解析bean标签时会为我们创建一个GenericBeanDefinition出来,用于存放bean标签解析出来的各种信息,所以,接下来我们有必要来了解下什么是GenericBeanDefinition。

BeanDefinition

首先来 看下BeanDefinition中有哪些东西

1.在spring中,每个对象都称之为bean,而bean是以BeanDefinition的形式存在
2.既然bean是以BeanDefinition形式存在,那BeanDefinition就是用来存放bean的基本信息,其实就是bean的定义

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {

    /**
     * Scope identifier for the standard singleton scope: {@value}.
     * 

Note that extended bean factories might support further scopes. * @see #setScope * @see ConfigurableBeanFactory#SCOPE_SINGLETON */ String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON; /** * Scope identifier for the standard prototype scope: {@value}. *

Note that extended bean factories might support further scopes. * @see #setScope * @see ConfigurableBeanFactory#SCOPE_PROTOTYPE */ String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE; /** * Role hint indicating that a {@code BeanDefinition} is a major part * of the application. Typically corresponds to a user-defined bean. */ int ROLE_APPLICATION = 0; //省略部分代码.....

既然BeanDefinition是一个接口,我们先来看看,BeanDefinition继承关系


BeanDefinition继承关系

可以看到,BeanDefinition接口的实现类为抽象类AbstractBeanDefinition,而AbstractBeanDefinition的实现类有三个,分别是ChildBeanDefinition、RootBeanDefinition、GenericBeanDefinition,其中,GenericBeanDefinition就是我们上面源码中初始化实现的BeanDefinition实现类
bean标签经过解析后,在spring容器中,刚开始是通过RootBeanDefinition,就比如,我们在applicationContext.xml文件中的配置


bean标签

因为bean标签中没有设置parent属性的值,也就是说它没有指定自己的父bean,所以可以使用RootBeanDefinition来封装该标签的信息,表示存放的是bean标签的根节点信息。
在RootBeanDefinition中有setParentName方法,如果parentName不为空,则会报错,所以,RootBeanDefinition在封装bean信息时,是不允许有父亲的。
RootBeanDefinition

根据上面的BeanDefinition继承关系,再来看下ChildBeanDefinition和RootBeanDefinition


ChildBeanDefinition

1.通过注释我们可以知道,从spring2.5之后,就会推荐使用GenericBeanDefinition方式进行注册bean
2.GenericBeanDefinition可以使用setParentName的方式,设置父bean的依赖

而AnnotatedGenericBeanDefinition其实简单来说,就是用来封装我们通过注解扫描来的bean,比如@Bean,@Service,@Component,@Repository

AbstractBeanDefinition作用BeanDefinition的抽象类,里面封装了很多公共的属性。如下:


AbstractBeanDefinition

看完BeanDefinition继承关系之后,我们接着来看parseBeanDefinitionElement中的方法,parseBeanDefinitionAttributes解析bean中的属性

/**
     * Apply the attributes of the given bean element to the given bean * definition.
     * @param ele bean declaration element
     * @param beanName bean name
     * @param containingBean containing bean definition
     * @return a bean definition initialized according to the bean element attributes
     */
    public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
            @Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {

        //获取属性singleton的值
        if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
            error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
        }
        else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
            bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
        }
        else if (containingBean != null) {
            // Take default from containing bean in case of an inner bean definition.
            bd.setScope(containingBean.getScope());
        }

        //获取属性abstract的值
        if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
            bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
        }

        //获取属性lazy-init
        String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
        if (isDefaultValue(lazyInit)) {
            lazyInit = this.defaults.getLazyInit();
        }
        bd.setLazyInit(TRUE_VALUE.equals(lazyInit));

        //获取属性autowire的值
        String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
        bd.setAutowireMode(getAutowireMode(autowire));

        //获取属性depends-on的值
        if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
            String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
            bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
        }

        //获取属性autowire-candidate的值
        String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);
        if (isDefaultValue(autowireCandidate)) {
            String candidatePattern = this.defaults.getAutowireCandidates();
            if (candidatePattern != null) {
                String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern);
                bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
            }
        }
        else {
            bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate));
        }

        // 获取属性primary的值
        if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
            bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
        }

        //获取属性init-method的值
        if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {
            String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);
            bd.setInitMethodName(initMethodName);
        }
        else if (this.defaults.getInitMethod() != null) {
            bd.setInitMethodName(this.defaults.getInitMethod());
            bd.setEnforceInitMethod(false);
        }

        //获取属性destroy-method
        if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {
            String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);
            bd.setDestroyMethodName(destroyMethodName);
        }
        else if (this.defaults.getDestroyMethod() != null) {
            bd.setDestroyMethodName(this.defaults.getDestroyMethod());
            bd.setEnforceDestroyMethod(false);
        }

        //获取属性factory-method的值
        if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
            bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
        }
        if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
            bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
        }

        return bd;
    }

解析完属性之后,接着就会解析子标签,meta、lookup-method、replaced-method、constructor-args、property和qualifier,其中我们比较熟悉的还是标签constructor-args和property

解析标签的逻辑大同小异,我们来看下是如何解析meta子标签的逻辑

    /**
     * Parse the meta elements underneath the given element, if any.
     */
    public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) {
        //获取bean标签下的所有子标签
        NodeList nl = ele.getChildNodes();
        for (int i = 0; i < nl.getLength(); i++) {
            Node node = nl.item(i);
            //遍历找到meta标签
            if (isCandidateElement(node) && nodeNameEquals(node, META_ELEMENT)) {
                Element metaElement = (Element) node;
                //获取属性key的值
                String key = metaElement.getAttribute(KEY_ATTRIBUTE);
                //获取属性value的值
                String value = metaElement.getAttribute(VALUE_ATTRIBUTE);
                //将key和value的值封装到BeanMetadataAttribute
                BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value);
                attribute.setSource(extractSource(metaElement));
                //将BeanMetadataAttribute添加到BeanMetadataAttributeAccessor
                attributeAccessor.addMetadataAttribute(attribute);
            }
        }
    }

可以看到,解析过程也是相当的清晰简单,就是遍历bean标签,然后找到meta标签。将解析到的mata标签下的key和value的值,封装到BeanMetadataAttribute,然后将BeanMetadataAttribute添加到BeanMetadataAttributeAccessor
AbstractBeanDefinition继承了BeanMetadataAttributeAccessor,BeanMetadataAtrribute和BeanMetadataAttributeAccessor,我们可以理解为是Bean封装属性和访问属性的底层类

parseLookupOverrideSubElements

/**
     * Parse lookup-override sub-elements of the given bean element.
     */
    public void parseLookupOverrideSubElements(Element beanEle, MethodOverrides overrides) {
        //获取bean标签下的所有子标签
        NodeList nl = beanEle.getChildNodes();
        //遍历所有的子标签
        for (int i = 0; i < nl.getLength(); i++) {
            Node node = nl.item(i);
            //在子标签中找到lookup-method标签
            if (isCandidateElement(node) && nodeNameEquals(node, LOOKUP_METHOD_ELEMENT)) {
                Element ele = (Element) node;
                //获取lookup-method标签的name属性值
                String methodName = ele.getAttribute(NAME_ATTRIBUTE);
                //获取属性bean的值
                String beanRef = ele.getAttribute(BEAN_ELEMENT);
                //将methodName和beanRef封装成LookupOverride
                LookupOverride override = new LookupOverride(methodName, beanRef);
                override.setSource(extractSource(ele));
                //黄LookupOverride添加到MethodOverrides
                overrides.addOverride(override);
            }
        }
    }

parseReplacedMethodSubElements

/**
     * Parse replaced-method sub-elements of the given bean element.
     */
    public void parseReplacedMethodSubElements(Element beanEle, MethodOverrides overrides) {
        //获取bean标签下的所有子标签
        NodeList nl = beanEle.getChildNodes();
        //遍历子标签
        for (int i = 0; i < nl.getLength(); i++) {
            Node node = nl.item(i);
            //找到子标签是replaced-method
            if (isCandidateElement(node) && nodeNameEquals(node, REPLACED_METHOD_ELEMENT)) {
                Element replacedMethodEle = (Element) node;
                //获取name的属性值
                String name = replacedMethodEle.getAttribute(NAME_ATTRIBUTE);
                //获取replacer的属性值
                String callback = replacedMethodEle.getAttribute(REPLACER_ATTRIBUTE);
                //将name的属性值和replacer的属性值封装成ReplaceOverride
                ReplaceOverride replaceOverride = new ReplaceOverride(name, callback);
                // Look for arg-type match elements.
                //进一步看下replaced-method标签下的arg-type子标签
                List argTypeEles = DomUtils.getChildElementsByTagName(replacedMethodEle, ARG_TYPE_ELEMENT);
                for (Element argTypeEle : argTypeEles) {
                    //获取子标签arg-type下的match的属性值
                    String match = argTypeEle.getAttribute(ARG_TYPE_MATCH_ATTRIBUTE);
                    match = (StringUtils.hasText(match) ? match : DomUtils.getTextValue(argTypeEle));
                    if (StringUtils.hasText(match)) {
                        //将属性match的值,一起封装的replaceOverride
                        replaceOverride.addTypeIdentifier(match);
                    }
                }
                replaceOverride.setSource(extractSource(replacedMethodEle));
                //将replaceOverride添加到MethodOverrides
                overrides.addOverride(replaceOverride);
            }
        }
    }

对于上面的三个标签,mata,lookup-method和replace-method,我们在实际开发过程中,几乎是用不到的,不比过多的去关注,如果好奇具体用法,可以自行搜索下。
对于bean的子标签,像constructor-arg,property和qualifier,我们实际用到的场景会比较多一些

我们来看下解析constructor-arg标签

/**
     * Parse constructor-arg sub-elements of the given bean element.
     */
    public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) {
        //获取bean标签下的所有子标签
        NodeList nl = beanEle.getChildNodes();
        for (int i = 0; i < nl.getLength(); i++) {
            Node node = nl.item(i);
            //遍历找到constructor-arg标签
            if (isCandidateElement(node) && nodeNameEquals(node, CONSTRUCTOR_ARG_ELEMENT)) {
                //解析constructor-arg标签
                parseConstructorArgElement((Element) node, bd);
            }
        }
    }

/**
     * Parse a constructor-arg element.
     */
    public void parseConstructorArgElement(Element ele, BeanDefinition bd) {
        //获取index的属性值
        String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE);
        //获取type的属性值
        String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE);
        //获取name的属性值
        String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
        //处理index存在的情况下
        if (StringUtils.hasLength(indexAttr)) {
            try {
                int index = Integer.parseInt(indexAttr);
                if (index < 0) {
                    error("'index' cannot be lower than 0", ele);
                }
                else {
                    try {
                        this.parseState.push(new ConstructorArgumentEntry(index));
                        //解析constructor-arg标签下的所有属性值
                        Object value = parsePropertyValue(ele, bd, null);
                        //将constructor-arg解析的所有属性值封装到ValueHolder中
                        ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
                        if (StringUtils.hasLength(typeAttr)) {
                            valueHolder.setType(typeAttr);
                        }
                        if (StringUtils.hasLength(nameAttr)) {
                            valueHolder.setName(nameAttr);
                        }
                        valueHolder.setSource(extractSource(ele));
                        //index的属性值不能重复,否则会混淆
                        if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) {
                            error("Ambiguous constructor-arg entries for index " + index, ele);
                        }
                        else {
                            //将constructor-arg标签解析到的所有信息,都封装在BeanDefinition
                            bd.getConstructorArgumentValues().addIndexedArgumentValue(index, valueHolder);
                        }
                    }
                    finally {
                        this.parseState.pop();
                    }
                }
            }
            catch (NumberFormatException ex) {
                error("Attribute 'index' of tag 'constructor-arg' must be an integer", ele);
            }
        }
        //处理属性name存在的情况
        else {
            try {
                this.parseState.push(new ConstructorArgumentEntry());
                //解析constructor-arg标签下的所有属性值
                Object value = parsePropertyValue(ele, bd, null);
                //将解析到的所有属性值,封装到ValueHolder中
                ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
                if (StringUtils.hasLength(typeAttr)) {
                    valueHolder.setType(typeAttr);
                }
                if (StringUtils.hasLength(nameAttr)) {
                    valueHolder.setName(nameAttr);
                }
                valueHolder.setSource(extractSource(ele));
                //将constructor-arg标签解析到的所有信息,都封装在BeanDefinition
                bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
            }
            finally {
                this.parseState.pop();
            }
        }
    }
  1. 可以看到,首先是看标签中的属性是index还是name,如果是index,就走if分支,否则就走else分支
    2.最后会把解析到的标签的属性值,封装到BeanDefinition中

我们来看下到底是如何具体解析constructor-arg标签的

/**
     * Get the value of a property element. May be a list etc.
     * Also used for constructor arguments, "propertyName" being null in this case.
     */
    @Nullable
    public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) {
        String elementName = (propertyName != null ?
                " element for property '" + propertyName + "'" :
                " element");

        // Should only have one child element: ref, value, list, etc.
        //获取当前节点的所有子标签
        NodeList nl = ele.getChildNodes();
        Element subElement = null;
        for (int i = 0; i < nl.getLength(); i++) {
            Node node = nl.item(i);
            //不处理description和meta标签
            if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) &&
                    !nodeNameEquals(node, META_ELEMENT)) {
                // Child element is what we're looking for.
                if (subElement != null) {
                    error(elementName + " must not contain more than one sub-element", ele);
                }
                else {
                    //记录子标签
                    subElement = (Element) node;
                }
            }
        }

        //是否包含ref属性
        boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
        //是否包含value属性
        boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
        //ref和value不能同时存在
        //或者是ref和value只存在一个,但是不能再有子标签
        if ((hasRefAttribute && hasValueAttribute) ||
                ((hasRefAttribute || hasValueAttribute) && subElement != null)) {
            error(elementName +
                    " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
        }

        // 如果ref标签存在
        if (hasRefAttribute) {
            //获取标签的属性值
            String refName = ele.getAttribute(REF_ATTRIBUTE);
            //ref属性存在,但是值不能为空
            if (!StringUtils.hasText(refName)) {
                error(elementName + " contains empty 'ref' attribute", ele);
            }
            RuntimeBeanReference ref = new RuntimeBeanReference(refName);
            ref.setSource(extractSource(ele));
            return ref;
        }
        //value属性的处理
        else if (hasValueAttribute) {
            TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
            valueHolder.setSource(extractSource(ele));
            return valueHolder;
        }
        else if (subElement != null) {
            //解析ele下面的所有子标签
            return parsePropertySubElement(subElement, bd);
        }
        else {
            // Neither child element nor "ref" or "value" attribute found.
            //ref和value的属性不能都不设置
            error(elementName + " must specify a ref or value", ele);
            return null;
        }
    }

1.首先是将description和meta标签剔除掉不处理,如果还存在子标签的话,就记录到subElement(如:array,list,set标签)
2.属性ref和value不能同时存在,因为ref代表引用另外一个bean,而value是代表某一个具体的值,所以只能二选一
3.如果ref和value都没有配置,而且也没有配置子标签的话,这样也是不行的,直接报错
4.如果子标签错在的话,最后会在else if (subElement != null) 中,解析子标签

parsePropertySubElement解析子标签

/**
     * Parse a value, ref or collection sub-element of a property or
     * constructor-arg element.
     * @param ele subelement of property element; we don't know which yet
     * @param bd the current bean definition (if any)
     */
    @Nullable
    public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd) {
        return parsePropertySubElement(ele, bd, null);
    }

    /**
     * Parse a value, ref or collection sub-element of a property or
     * constructor-arg element.
     * @param ele subelement of property element; we don't know which yet
     * @param bd the current bean definition (if any)
     * @param defaultValueType the default type (class name) for any
     * {@code } tag that might be created
     */
    @Nullable
    public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd, @Nullable String defaultValueType) {
        if (!isDefaultNamespace(ele)) {
            return parseNestedCustomElement(ele, bd);
        }
        //处理便签bean
        else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
            BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
            if (nestedBd != null) {
                nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
            }
            return nestedBd;
        }
        //处理ref标签
        else if (nodeNameEquals(ele, REF_ELEMENT)) {
            // A generic reference to any name of any bean.
            String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);
            boolean toParent = false;
            if (!StringUtils.hasLength(refName)) {
                // A reference to the id of another bean in a parent context.
                refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);
                toParent = true;
                if (!StringUtils.hasLength(refName)) {
                    error("'bean' or 'parent' is required for  element", ele);
                    return null;
                }
            }
            if (!StringUtils.hasText(refName)) {
                error(" element contains empty target attribute", ele);
                return null;
            }
            RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);
            ref.setSource(extractSource(ele));
            return ref;
        }
        //处理idref标签
        else if (nodeNameEquals(ele, IDREF_ELEMENT)) {
            return parseIdRefElement(ele);
        }
        //处理value标签
        else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
            return parseValueElement(ele, defaultValueType);
        }
        //处理null标签
        else if (nodeNameEquals(ele, NULL_ELEMENT)) {
            // It's a distinguished null value. Let's wrap it in a TypedStringValue
            // object in order to preserve the source location.
            TypedStringValue nullHolder = new TypedStringValue(null);
            nullHolder.setSource(extractSource(ele));
            return nullHolder;
        }
        //处理array标签
        else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
            return parseArrayElement(ele, bd);
        }
        //处理list标签
        else if (nodeNameEquals(ele, LIST_ELEMENT)) {
            return parseListElement(ele, bd);
        }
        //处理set标签
        else if (nodeNameEquals(ele, SET_ELEMENT)) {
            return parseSetElement(ele, bd);
        }
        //处理map标签
        else if (nodeNameEquals(ele, MAP_ELEMENT)) {
            return parseMapElement(ele, bd);
        }
        //处理props标签
        else if (nodeNameEquals(ele, PROPS_ELEMENT)) {
            return parsePropsElement(ele);
        }
        else {
            error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
            return null;
        }
    }

其实对于property标签的处理结果也是类似

总结

初级容器加载流程

你可能感兴趣的:(3.spring初级容器XmlBeanFactory初始化(二))