Spring源码分析-配置文件的解析(二)

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】
     

上一节介绍了资源的定位,既然资源已经定位了,本章将进入具体的解析工作,具体的解析工作是从XmlBeanDefinitionReader类的loadBeanDefinitions(Resource resource)方法开始。

一. loadBeanDefinitions(Resource resource)源码如下 XmlBeanDefinitionReader类

    public int loadBeanDefinitions(Resource resource)
            throws BeanDefinitionStoreException
        {
            return loadBeanDefinitions(new EncodedResource(resource));
        }

EncodedResource是对Resource的封装,采用装饰模式,主要是增加了编码的信息.

        /**
         * Load bean definitions from the specified XML file.
         * @param encodedResource the resource descriptor for the XML file,
         * allowing to specify an encoding to use for parsing the file
         * @return the number of bean definitions found
         * @throws BeanDefinitionStoreException in case of loading or parsing errors
         */
        public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
            Assert.notNull(encodedResource, "EncodedResource must not be null");
            if (logger.isInfoEnabled()) {
                logger.info("Loading XML bean definitions from " + encodedResource.getResource());
            }

            Set currentResources = (Set) this.resourcesCurrentlyBeingLoaded.get();
            if (currentResources == null) {
                currentResources = new HashSet(4);
                this.resourcesCurrentlyBeingLoaded.set(currentResources);
            }
            if (!currentResources.add(encodedResource)) {
                throw new BeanDefinitionStoreException(
                        "Detected recursive loading of " + encodedResource + " - check your import definitions!");
            }
            try {
                InputStream inputStream = encodedResource.getResource().getInputStream();
                try {
                    InputSource inputSource = new InputSource(inputStream);
                    if (encodedResource.getEncoding() != null) {
                        inputSource.setEncoding(encodedResource.getEncoding());
                    }
                    return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
                }
                finally {
                    inputStream.close();
                }
            }
            catch (IOException ex) {
                throw new BeanDefinitionStoreException(
                        "IOException parsing XML document from " + encodedResource.getResource(), ex);
            }
            finally {
                currentResources.remove(encodedResource);
                if (currentResources.isEmpty()) {
                    this.resourcesCurrentlyBeingLoaded.set(null);
                }
            }
        }

这个方法里最核心的操作是doLoadBeanDefinitions(inputSource, encodedResource.getResource())

        /**
         * 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
         */
        protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
                throws BeanDefinitionStoreException {
            try {
                int validationMode = getValidationModeForResource(resource);
                Document doc = this.documentLoader.loadDocument(
                        inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
                return registerBeanDefinitions(doc, resource);
            }
            catch (BeanDefinitionStoreException ex) {
                throw ex;
            }
        }

DocumentLoader类主要负责把一个XML文档解析成一个Document对象,注册beanDefinitions的工作交给registerBeanDefinitions方法了.

        /**
         * Register the bean definitions contained in the given DOM document.
         * Called by <code>loadBeanDefinitions</code>.
         * <p>Creates a new instance of the parser class and invokes
         * <code>registerBeanDefinitions</code> on it.
         * @param doc the DOM document
         * @param resource the resource descriptor (for context information)
         * @return the number of bean definitions found
         * @throws BeanDefinitionStoreException in case of parsing errors
         * @see #loadBeanDefinitions
         * @see #setDocumentReaderClass
         * @see BeanDefinitionDocumentReader#registerBeanDefinitions
         */
        public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
            // Support old XmlBeanDefinitionParser SPI for backwards-compatibility.
            if (this.parserClass != null) {
                XmlBeanDefinitionParser parser =
                        (XmlBeanDefinitionParser) BeanUtils.instantiateClass(this.parserClass);
                return parser.registerBeanDefinitions(this, doc, resource);
            }
            // Read document based on new BeanDefinitionDocumentReader SPI.
            BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
            int countBefore = getRegistry().getBeanDefinitionCount();
            documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
            return getRegistry().getBeanDefinitionCount() - countBefore;
        }

实际上XmlBeanDefinitionReader把解析注册的工作又转交给BeanDefinitionDocumentReader来完成了。

这里getRegistry()实际上返回的是上一节介绍的生成的DefaultListableBeanFactory,这个工厂实际上是在生成XmlBeanDefinitionReader对象时通过构造方法入参传入的。

这里的createReaderContext(Resource resource)应该特别注意一下,这个上下文中包含了很多重要的信息,以提供给BeanDefinitionDocumentReader来使用。

        /**
         * Create the {@link XmlReaderContext} to pass over to the document reader.
         */
        protected XmlReaderContext createReaderContext(Resource resource) {
            if (this.namespaceHandlerResolver == null) {
                this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
            }
            return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
                    this.sourceExtractor, this, this.namespaceHandlerResolver);
        }

二:我们来看看DefaultBeanDefinitionDocumentReader是如何来registerBeanDefinitions的.

        public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext)
        {
            this.readerContext = readerContext;
            logger.debug("Loading bean definitions");
            Element root = doc.getDocumentElement();
            BeanDefinitionParserDelegate delegate = createHelper(readerContext, root);
            preProcessXml(root);
            parseBeanDefinitions(root, delegate);
            postProcessXml(root);
        }

可以看出DefaultBeanDefinitionDocumentReader又把解析注册的工作委托给BeanDefinitionParserDelegate来处理了,具体的工作见parseBeanDefinitions方法.

        protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)
        {
            if(delegate.isDefaultNamespace(root.getNamespaceURI()))
            {
                NodeList nl = root.getChildNodes();
                for(int i = 0; i < nl.getLength(); i++)
                {
                    org.w3c.dom.Node node = nl.item(i);
                    if(node instanceof Element)
                    {
                        Element ele = (Element)node;
                        String namespaceUri = ele.getNamespaceURI();
                        if(delegate.isDefaultNamespace(namespaceUri))
                            parseDefaultElement(ele, delegate);
                        else
                            delegate.parseCustomElement(ele);
                    }
                }

            } else
            {
                delegate.parseCustomElement(root);
            }
        }

我们继续来看一下核心操作parseDefaultElement(ele,delegate)方法。

        private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate)
        {
            if(DomUtils.nodeNameEquals(ele, "import"))
                importBeanDefinitionResource(ele);
            else
            if(DomUtils.nodeNameEquals(ele, "alias"))
                processAliasRegistration(ele);
            else
            if(DomUtils.nodeNameEquals(ele, "bean"))
                processBeanDefinition(ele, delegate);
        }

Xml配置文件中的bean的解析注册就在processBeanDefinition中了。

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

Spring把每一个Bean结点解析之后,生成底层的数据结构BeanDefinition,然后存储在BeanDefinitionHold中,很明显具体的真正的解析工作放在parseBeanDefinitionElement方法中,解析完成后则调用BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry())把beanDefinition注册到beanFactory中。下面,我们来看一下核心方法parseBeanDefinitionElement的源码.

三:我们来看看BeanDefinitionParserDelegate的parseBeanDefinitionElement(Element ele)是如何来解析的.

    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele)
        {
            return parseBeanDefinitionElement(ele, null);
        }

        public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean)
        {
            String id = ele.getAttribute("id");
            String nameAttr = ele.getAttribute("name");
            List aliases = new ArrayList();
            if(StringUtils.hasLength(nameAttr))
            {
                String nameArr[] = StringUtils.tokenizeToStringArray(nameAttr, ",; ");
                aliases.addAll(Arrays.asList(nameArr));
            }
            String beanName = id;
            if(!StringUtils.hasText(beanName) && !aliases.isEmpty())
            {
                beanName = (String)aliases.remove(0);
                if(logger.isDebugEnabled())
                    logger.debug("No XML 'id' specified - using '" + beanName + "' as bean name and " + aliases + " as aliases");
            }
            if(containingBean == null)
                checkNameUniqueness(beanName, aliases, ele);
            AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
            if(beanDefinition != null)
            {
                if(!StringUtils.hasText(beanName))
                    try
                    {
                        if(containingBean != null)
                        {
                            beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, readerContext.getRegistry(), true);
                        } else
                        {
                            beanName = readerContext.generateBeanName(beanDefinition);
                            String beanClassName = beanDefinition.getBeanClassName();
                            if(beanClassName != null && beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && !readerContext.getRegistry().isBeanNameInUse(beanClassName))
                                aliases.add(beanClassName);
                        }
                        if(logger.isDebugEnabled())
                            logger.debug("Neither XML 'id' nor 'name' specified - using generated bean name [" + beanName + "]");
                    }
                    catch(Exception ex)
                    {
                        error(ex.getMessage(), ele);
                        return null;
                    }
                String aliasesArray[] = StringUtils.toStringArray(aliases);
                return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
            } else
            {
                return null;
            }
        }

在这里我们看到对id,name等属性的解析,最核心的操作为 AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);

        /**
         * Parse the bean definition itself, without regard to name or aliases. May return
         * <code>null</code> if problems occured during the parse of the bean definition.
         */
        public AbstractBeanDefinition parseBeanDefinitionElement(
                Element ele, String beanName, BeanDefinition containingBean) {

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

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

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

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

                parseMetaElements(ele, bd);
                parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
                parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

                parseConstructorArgElements(ele, bd);
                parsePropertyElements(ele, bd);
                parseQualifierElements(ele, bd);

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

                return bd;
            }
            catch (ClassNotFoundException ex) {
                error("Bean class [" + className + "] not found", ele, ex);
            }
            catch (NoClassDefFoundError err) {
                error("Class that bean class [" + className + "] depends on not found", ele, err);
            }
            catch (Throwable ex) {
                error("Unexpected failure during bean definition parsing", ele, ex);
            }
            finally {
                this.parseState.pop();
            }

            return null;
        }

在这段代码中,你可清楚的看到对bean组件相关属性和子标记的解析和处理(根据方法的命名).

四:在这里,可以重点关注一下ParseState

    public final class ParseState
    {
        public static interface Entry
        {
        }

        public ParseState()
        {
            state = new Stack();
        }

        private ParseState(ParseState other)
        {
            state = (Stack)other.state.clone();
        }

        public void push(Entry entry)
        {
            state.push(entry);
        }

        public void pop()
        {
            state.pop();
        }

        public Entry peek()
        {
            return (Entry)(state.empty() ? null : state.peek());
        }

        public ParseState snapshot()
        {
            return new ParseState(this);
        }

        public String toString()
        {
            StringBuffer sb = new StringBuffer();
            for(int x = 0; x < state.size(); x++)
            {
                if(x > 0)
                {
                    sb.append('\n');
                    for(int y = 0; y < x; y++)
                        sb.append('\t');

                    sb.append("-> ");
                }
                sb.append(state.get(x));
            }

            return sb.toString();
        }

        private static final char TAB = 9;
        private final Stack state;
    }

这个类用了比较巧妙的方式来跟踪解析过程中的相关步骤,用户详见源码和API。

Simple Stack-based structure for tracking the logical position during a parsing process. entries are added to the stack at each point during the parse phase in a reader-specific manner.

Calling toString() will render a tree-style view of the current logical position in the parse phase. This representation is intended for use in error messages.


来源:http://ddrv.cn

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » Spring源码分析-配置文件的解析(二)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏