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对象,这里还有一个委派模式来寻求解析处理器的操作,我们将在后面统一分析,欢迎交流。