所有文章已迁移至csdn,csdn个人主页https://blog.csdn.net/chaitoudaren
// XmlBeanDefinitionReader.java
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
// 1.将文件流转化为DOM树
Document doc = doLoadDocument(inputSource, resource);
// 2.DOM树转化为BeanDefinition,并注册到对应map中
int count = registerBeanDefinitions(doc, resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
}
catch () {
throw ...;
}
}
上节我们讲了inputStream -> DOM树的过程,也就是备注的第一点,现在我们从第二点,开始继续往下阅读。
// XmlBeanDefinitionReader.java
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
// 真正开始解析DOM树,生产并注册BeanDefinitions
doRegisterBeanDefinitions(doc.getDocumentElement());
}
DOM树 -> BeanDefinition
- 创建解析DOM树的委托(delegate)
- spring委托delegate进行DOM树的解析,其中需要注意的是<beans>存在嵌套,假如子<beans>中无default-*的相关信息,默认他需要继承外层<beans>的配置信息
- preProcessXml,postProcessXml是2个没有final修饰的空方法(该设计思想非常重要,画重点!),典型的设计模式中的模板方法模式。该方法留给子类继承并完成override,让用户有机会能够干预spring的各个环节。spring大量的使用这种设计模式,包括我们后面要重点讲的后置处理器(BeanPostProcessor)
// DefaultBeanDefinitionDocumentReader.java
protected void doRegisterBeanDefinitions(Element root) {
/**
* delegate 指的是委托的意思,spring委托他进行DOM树的解析
*
* Xml文件中的<beans>标签是允许嵌套的,当出现嵌套时势必引起函数的递归调用,
* 为了防止外层<beans>标签的default-*相关信息在子委托中丢失,确保内存<beans>能正确继承default-*相关信息
* spring使用了堆栈的思想,先保存父委托,在将父委托传入以确保子委托的正确继承创建,函数最后即子委托结束后,再还原父委托
*/
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) {
/**
* profile属性,用于在一个xml中实现多套配置
* 例如切换开发和生产环境:
* <beans profile = "dev"> ... </beans>
* <beans profile = "pro"> ... </beans>
*/
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
// We cannot use Profiles.of(...) since profile expressions are not supported
// in XML config. See SPR-12458 for details.
// 尝试在环境中指定profile可以接收的bean
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
// 预留给子类实现,在解析前进行自定义操作
preProcessXml(root);
// 解析及注册BeanDefinition,核心逻辑
parseBeanDefinitions(root, this.delegate);
// 预留给子类,解析后的操作
postProcessXml(root);
this.delegate = parent;
}
- 解析DOM树
为了更好抓住主线,我们只分析默认名称空间的解析
// DefaultBeanDefinitionDocumentReader.java
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
// 默认名称空间,本质是比较namespace是否等于"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)) {
parseDefaultElement(ele, delegate);
}
else {
// 自定义名称空间
delegate.parseCustomElement(ele);
}
}
}
}
else {
// 自定义名称空间
delegate.parseCustomElement(root);
}
}
- 解析默认名称空间
代码阅读到这里我们看到了几个熟悉的常量,这里不打算每个标签都进行讲解,还是抓住主线
- import:<import resource = "config.xml"> 该标签递归调用加载资源,将重点分析
- bean:spring最重要的标签,重点分析
- alias:别名标签,该标签留给读者自行分析
- beans:该标签递归调用doRegisterBeanDefinitions,这里将会用到之前讲到的<beans>标签嵌套,以及parent delegate继承的使用。留给读者自行分析
// DefaultBeanDefinitionDocumentReader.java
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标签,解析该标签实际是递归调用doRegisterBeanDefinitions,前面提到的parent
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
至此spring解析标签的准备工作才算做完,接下去标签的解析、注册才算刚刚开始
网友评论