Spring配置文件解析(三)BeanDefinitionDocumentReader源码分析

1 概述

前面我们分析了BeanDefinitionReader的源码(Spring配置文件解析(二)BeanDefinitionReader源码分析 ),我们知道BeanDefinitionReader的作用其实就是将Spring的Bean定义的XML文件最终解析成Document对象,然后将这个对象交给BeanDefinitionDocumentReader来完成最终的XML文档的解析和Bean定义的注册。
所以BeanDefinitionDocumentReader接口的功能很简单,就是解析XML文档对象Document,并且完成bean定义的注册。
在继续分析解析Document对象的逻辑之前,我们依然先来对BeanDefinitionDocumentReader进行一个整体了解。

2 函数

这个接口的函数比较简单,只有一个。

void registerBeanDefinitions(Document doc, XmlReaderContext readerContext)
		throws BeanDefinitionStoreException;


这个函数的作用就是从Document对象中读取Bean定义,并在给定的阅读器上下文中向注册中心注册它们。

3 派生类

针对BeanDefinitionDocumentReader的派生类比较简单就只有DefaultBeanDefinitionDocumentReader一个。
DefaultBeanDefinitionDocumentReader:
这个类是BeanDefinitionDocumentReader接口的默认实现,根据"spring-beans"的DTD和XSD格式来读取Bean定义。
这个XML文档要求的结构、元素和属性被硬编码在了类里面,所以我们可以看下在DefaultBeanDefinitionDocumentReader类中有如下常量属性:
 

public static final String NESTED_BEANS_ELEMENT = "beans";
public static final String ALIAS_ELEMENT = "alias";
public static final String NAME_ATTRIBUTE = "name";
public static final String ALIAS_ATTRIBUTE = "alias";
public static final String IMPORT_ELEMENT = "import";
public static final String RESOURCE_ATTRIBUTE = "resource";
public static final String PROFILE_ATTRIBUTE = "profile";

在BeanDefinitionDocumentReader类的属性中,我们可以看见还有XmlReaderContext和BeanDefinitionParserDelegate这两个属性,那么他们是干什么用的呢?
(1)XmlReaderContext
XmlReaderContext类继承自ReaderContext,ReaderContext是在bean定义读取过程中传递的上下文,它封装所有相关配置和状态。XmlReaderContext提供了对XmlBeanDefinitionReader中的NamespaceHandlerResolver对象的访问路径,当然这个的提供方式我们可以看XmlBeanDefinitionReader中的如下函数发现:

public XmlReaderContext createReaderContext(Resource resource) {
		return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
			this.sourceExtractor, this, getNamespaceHandlerResolver());
}

关于NamespaceHandlerResolver的分析,我们在后面的文章会提到。
(2)BeanDefinitionParserDelegate
其实XML中各个元素的解析是由BeanDefinitionParserDelegate来完成的。

4 配置文件解析分析

我们接折上一篇文章分析Spring中配置文件的解析。
Document对象的解析和Bean定义的注册由public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext)函数来完成。

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
	this.readerContext = readerContext;
	logger.debug("Loading bean definitions");
	Element root = doc.getDocumentElement();
	doRegisterBeanDefinitions(root);
}

这个函数用于根据"spring-beans" XSD(或者DTD)转换bean定义,而针对aop等相关的配置是在什么地方解析处理的,其实他们都是通过NamespaceHandler实现类来完成的,我们后面会进行分析。
上面的函数,在获取到root之后就调用protected void doRegisterBeanDefinitions(Element root)函数。

protected void doRegisterBeanDefinitions(Element root) {
		
	//创建BeanDefinitionParserDelegate,其实XML中各个元素的解析是由BeanDefinitionParserDelegate来完成的。
	this.delegate = createDelegate(getReaderContext(), root, parent);

	//验证root是否是beans节点
	if (this.delegate.isDefaultNamespace(root)) {
		String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
		
		//判断root的环境变量profile是否为空
		if (StringUtils.hasText(profileSpec)) {
			String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
					profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
					
			//判断Spring容器支持的环境是否有当前环境		
			if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
				if (logger.isInfoEnabled()) {
					logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
							"] not matching: " + getReaderContext().getResource());
				}
				return;
			}
		}
	}

	preProcessXml(root);
	
	//转换bean定义
	parseBeanDefinitions(root, this.delegate);
	postProcessXml(root);

	this.delegate = parent;
}

 

上面的函数的作用其实就是验证当前的root节点能否被解析。最终的解析工作留给了parseBeanDefinitions函数。

/**
 * 解析根节点下的"import", "alias", "bean"元素
 */
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {

    //验证root是否是beans节点
	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)) {
				
				    //默认的一些标签在DefaultBeanDefinitionDocumentReader进行解析
					parseDefaultElement(ele, delegate);
				}
				else {
				
				    //如果不是默认的就需要在通过委派模式来寻找解析处理器
					delegate.parseCustomElement(ele);
				}
			}
		}
	}
	else {
	
	    //如果不是默认的就需要在通过委派模式来寻找解析处理器
		delegate.parseCustomElement(root);
	}
}


上面是否是默认的标签,其实就是判断命名空间是否是和”http://www.springframework.org/schema/beans“相等。
在parseDefaultElement中会对一些默认的标签import,alias,bean,beans标签进行处理。

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
	
	//解析import标签
	if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
		importBeanDefinitionResource(ele);
	}
	
	//解析alias标签
	else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
		processAliasRegistration(ele);
	}
	
	//解析bean标签
	else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
		processBeanDefinition(ele, delegate);
	}
	
	//递归解析beans标签
	else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
		// recurse
		doRegisterBeanDefinitions(ele);
	}
}


对bean的解析最终调用protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate)函数。

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {

    //最终还是在BeanDefinitionParserDelegate中进行元素解析
	//BeanDefinitionHolder是对BeanDefinition的封装,包括BeanDefinition,beanName,aliases
	BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
	if (bdHolder != null) {
		bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
		try {
			//处理的操作是将bean的元数据BeanDifinition注册到BeanFactory中。
			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));
	}
}


至此,我们小结一下,其实可以看出,BeanDefinitionDocumentReader并没有对XML文档的Document对象进行解析,最终交给了BeanDefinitionParserDelegate对象,这里还有一个委派模式来寻求解析处理器的操作,我们将在后面统一分析,欢迎交流。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值