美文网首页
3.spring初级容器XmlBeanFactory初始化(二)

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

作者: T_log | 来源:发表于2022-08-31 17:11 被阅读0次

    开篇

    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.
         * <p>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.
         * <p>If given a plain BeanDefinitionRegistry, the default ResourceLoader will be a
         * {@link org.springframework.core.io.support.PathMatchingResourcePatternResolver}.
         * <p>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.
         * <p>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).
         * <p>Opens a DOM Document; then initializes the default settings
         * specified at the {@code <beans/>} 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 <beans/>} element.
         */
        @SuppressWarnings("deprecation")  // for Environment.acceptsProfiles(String...)
        protected void doRegisterBeanDefinitions(Element root) {
            // Any nested <beans> elements will cause recursion in this method. In
            // order to propagate and preserve <beans> 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中有很多自己自定义的标签,如:<tx:annotation-driven/>,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 <bean>} element. May return {@code null}
         * if there were errors during parse. Errors are reported to the
         * {@link org.springframework.beans.factory.parsing.ProblemReporter}.
         */
        @Nullable
        public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
            return parseBeanDefinitionElement(ele, null);
        }
    
    /**
         * Parses the supplied {@code <bean>} element. May return {@code null}
         * if there were errors during parse. Errors are reported to the
         * {@link org.springframework.beans.factory.parsing.ProblemReporter}.
         */
        @Nullable
        public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
            //1.解析bean标签中的id和name属性
            String id = ele.getAttribute(ID_ATTRIBUTE);
            String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
    
            //2.将属性name通过","或";"分隔符进行分割,并将数据添加到aliases中
            List<String> aliases = new ArrayList<>();
            if (StringUtils.hasLength(nameAttr)) {
                String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
                aliases.addAll(Arrays.asList(nameArr));
            }
    
            // 3.如果属性id为空,那么就取aliases集合中的第一个value的值,作为bean的名称
            String beanName = id;
            if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
                beanName = aliases.remove(0);
                if (logger.isTraceEnabled()) {
                    logger.trace("No XML 'id' specified - using '" + beanName +
                            "' as bean name and " + aliases + " as aliases");
                }
            }
    
            if (containingBean == null) {
                checkNameUniqueness(beanName, aliases, ele);
            }
    
            // 4. 开始解析bean标签,并将解析结果封装为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}.
         * <p>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}.
         * <p>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<Element> 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 ?
                    "<property> element for property '" + propertyName + "'" :
                    "<constructor-arg> 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 <value>} 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 <ref> element", ele);
                        return null;
                    }
                }
                if (!StringUtils.hasText(refName)) {
                    error("<ref> 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初始化(二)

          本文链接:https://www.haomeiwen.com/subject/jwyegrtx.html