- 前言
- 第一部分 核心实现
- 第 1 章 Spring 整体架构和环境搭建
- 第 2 章 容器的基本实现
- 第 3 章 默认标签的解析
- 第 4 章 自定义标签的解析
- 第 5 章 bean 的加载
- 第 6 章 容器的功能扩展
- 第 7 章 AOP
- 第二部分 企业应用
- 第 8 章 数据库连接 JDBC
- 第 9 章 整合 MyBatis
- 第 10 章 事务
- 第 11 章 SpringMVC
- 第 12 章 远程服务
- 第 13 章 Spring 消息
2.8 解析及注册 BeanDefinitions
当把文件转换为 Document 后,接下来的提取及注册 bean 就是我们的重头戏。继续上面的分析,当程序已经拥有 XML 文档文件的 Document 实例对象时,就会被引入下面这个方法。
XmlBeanDefinitionReader.java
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStore
Exception {
//使用 DefaultBeanDefinitionDocumentReader 实例化 BeanDefinitionDocumentReader
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
//将环境变量设置其中
documentReader.setEnvironment(this.getEnvironment());
//在实例化 BeanDefinitionReader 时候会将 BeanDefinitionRegistry 传入,默认使用继承自
DefaultListableBeanFactory 的子类
//记录统计前 BeanDefinition 的加载个数
int countBefore = getRegistry().getBeanDefinitionCount();
//加载及注册 bean
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
//记录本次加载的 BeanDefinition 个数
return getRegistry().getBeanDefinitionCount() - countBefore;
}
其中的参数 doc 是通过上一节 loadDocument 加载转换出来的。在这个方法中很好地应用了面向对象中单一职责的原则,将逻辑处理委托给单一的类进行处理,而这个逻辑处理类就是 BeanDefinitionDocumentReader。 BeanDefinitionDocumentReader 是一个接口,而实例化的工作是在 createBeanDefinitionDocumentReader() 中完成的,而通过此方法,BeanDefinitionDocumentReader 真正的类型其实已经是 DefaultBeanDefinitionDocumentReader 了,进入 DefaultBeanDefinition Document Reader 后,发现这个方法的重要目的之一就是提取 root,以便于再次将 root 作为参数继续 BeanDefinition 的注册。
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
Element root = doc.getDocumentElement();
doRegisterBeanDefinitions(root);
}
经过艰难险阻,磕磕绊绊,我们终于到了核心逻辑的底部 doRegisterBeanDefinitions(root),至少我们在这个方法中看到了希望。
如果说以前一直是 XML 加载解析的准备阶段,那么 doRegisterBeanDefinitions 算是真正地开始进行解析了,我们期待的核心部分真正开始了。
protected void doRegisterBeanDefinitions(Element root) {
//处理 profile 属性
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
Assert.state(this.environment != null, "environment property must not be null");
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec,
BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
if (!this.environment.acceptsProfiles(specifiedProfiles)) {
return;
}
}
//专门处理解析
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createHelper(readerContext, root, parent);
//解析前处理,留给子类实现
preProcessXml(root);
parseBeanDefinitions(root, this.delegate);
//解析后处理,留给子类实现
postProcessXml(root);
this.delegate = parent;
}
通过上面的代码我们看到了处理流程,首先是对 profile 的处理,然后开始进行解析,可是当我们跟进 preProcessXml(root) 或者 postProcessXml(root) 发现代码是空的,既然是空的写着还有什么用呢?就像面向对象设计方法学中常说的一句话,一个类要么是面向继承的设计的,要么就用 final 修饰。在 DefaultBeanDefinitionDocumentReader 中并没有用 final 修饰,所以它是面向继承而设计的。这两个方法正是为子类而设计的,如果读者有了解过设计模式,可以很快速地反映出这是模版方法模式,如果继承自 DefaultBeanDefinitionDocumentReader 的子类需要在 Bean 解析前后做一些处理的话,那么只需要重写这两个方法就可以了。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论