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

 

 

上一节介绍了资源的定位,既然资源已经定位了,本章将进入具体的解析工作,具体的解析工作是从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.

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值