把文件转换为Document后,接下来的提取及注册bean就是我们的重头戏。
XmlBeanDefinitionReader.java
@SuppressWarnings("deprecation")
public int registerBeanDefinitions (Document doc, Resource resource) throws BeanDefinitionStoreException {
//todo使用efaultBeanDefinitionDocumentReader实例化BeanDefinitionDocumentReader
BeanDefinitionDocumentReader documentReader =createBeanDefinitionDocumentReader();
//todo将环境变量设置其中,environment在AbstractBeanDefinitionReader初始化的时候初始化的
documentReader.setEnvironment (getEnvironment());
//todo在实例化BandDefinitionReader时候会将BeanDefinitionRegister传入(在AbstractBeanDefinitionReader中传入的),默认使用继承自DefaultListableBeanfactory的子类
//todo.记录统计前BeanDefinition的加载个数
int countBefore= getRegistry().getBeanDefinitionCount ();//getRegistry()获取的是BeanDefinitionRegistry
//todo.加截及注册ean
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));//todo记录本次加载的BeanDefinition个数
return getRegistry().getBeanDefinitionCount() - countBefore;
DefaultBeanDefinitionDocumentReader.class
protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader()
return BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass));
}
其中参数doc是通过上一节获取的loadDocument加载转换出来的。
BeanDefinitionDocumentReader中重要的目的之一就是提取root,以便再次将root作为参数继续BeanDefinition的注册
DefaultBeanDefinitionDocumentReader.java
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
Element root = doc.getDocumentElement();
doRegisterBeanDefinitions(root);
}
前面都是为XML加载解析的准备阶段,那么doRegisterBeanDefinitions就是正真地开始进行解析了
protected void doRegisterBeanDefinitions(Element root) {
//todo.处理profile届性
string profilespec = root.getAttribute(PROFILE ATTRIBUTE);
if (stringutils.hasText(profilespec)) {
string[] specifiedProfiles = stringutils.tokenizeTostringArray(
profilespec. BeanDefinitionParserDelegate.MULTI VALUE ATTRIBUTE DELIMITERS);
if. (!getEnvironment().acceptsProfiles(specifiedProfiles)) {
return;
}
//todo 专门处理解析
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate. = createDelegate(this.readercontext, root, parent);
//todo.解析前处理,贸给子类实现
preProcessXml(root);
parseBeanDefinitions(root, this.delegate);
//todo.解析后处理,留給子类实现
postprocessxml(root);
this.delegate= parent;
}
上面的代码,首先对profile进行处理,然后开始解析。其中的preProcessXml(root)和postProcessXml(root)使用了模板设计模式,开发者可以继承这些方法来增加对Bean解析的处理需要。
1.profile属性的使用
spring的实例代码如下:
<beans profile="dev">
... ...
</beans>
<beans profile="production">
... ...
</beans>
集成到Web环境中,在web.xml中加入以下代码
<context-param>
<param-name>Spring.profile.active</param-name>
<param-value>dev</param-value>
</context-param>
有了这个特性我们就可以同时在配置文件中部署两套配置来适用于生产环境和开发环境。
在代码中程序会获取beans节点是否定义了profile属性,如果定义了则会需要到环境变量中寻找,所以首先断言environment不可能为空,因为profile是可以同时制定多个的,需要程序对其拆分,并解析每个profile是都符合环境变量中所定义的,不定义则不会浪费性能去解析。
2.解析并注册BeanDefinition
处理了profile之后就可进行xml的读取
protected void parseBeanDefinitions (Element root, BeanDefinitionParserDelegate delegate) {
//todo.对默认bean的处理,即获取的节点的URI中包含http://www.springframework.org/schema/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)) {
//todo.对bean的处理
parseDefaultElement(ele, delegate);
}
else {
//todo.对bean的处理
delegate.parseCustomElement(ele);
}
}
}
}
else {
//对自定义标签的处理
delegate.parseCustomElement(root);
}
}
因为在Spring的XML配置里面有两大类Bean声明,一个是默认的:
<bean id="test" class="test.TestBean" />
另一类就是自定义的,如:
<tx:annotation-driven/>
这两种方式的读写以及解析差别是非常大的,如果采用Spring默认的配置,Spring会按照自己的逻辑进行处理。
如果是自定义的标签,那么用户就需要实现一些接口以及配置了。
判断是否默认命名空间还是自定义命名空间的办法其实是使用node.getNamespaceURI()获取命名空间,并于Spring中固定的命名空间进行对比,如果一直,那么就是默认,不一致就是自定义了
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
//获取节点对应的URI
String namespaceUri = getNamespaceURI(ele);
//根据URI去寻找对应的NamespaceHandler,进行处理,这个NamespaceHandler为用户自己实现NamespaceHandler类的handler类
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}