BeanDefinitionDocumentReader 接口通过registerBeanDefinitions() 方法调用其实现类DefaultBeanDefinitionDocumentReader 对Document 对象进行解析,解析的代码如下:

//根据Spring DTD对Bean的定义规则解析Bean定义Document对象
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {//获得XML描述符this.readerContext = readerContext;logger.debug("Loading bean definitions");//获得Document的根元素Element root = doc.getDocumentElement();doRegisterBeanDefinitions(root);
}
protected void doRegisterBeanDefinitions(Element root) {// Any nested <beans> elements will cause recursion in this method. In// order to propagate and preserve <beans> default-* attributes correctly,// keep track of the current (parent) delegate, which may be null. Create// the new (child) delegate with a reference to the parent for fallback purposes,// then ultimately reset this.delegate back to its original (parent) reference.// this behavior emulates a stack of delegates without actually necessitating one.//具体的解析过程由BeanDefinitionParserDelegate实现,//BeanDefinitionParserDelegate中定义了Spring Bean定义XML文件的各种元素BeanDefinitionParserDelegate parent = this.delegate;this.delegate = createDelegate(getReaderContext(), root, parent);if (this.delegate.isDefaultNamespace(root)) {String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);if (StringUtils.hasText(profileSpec)) {String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);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;}}}//在解析Bean定义之前,进行自定义的解析,增强解析过程的可扩展性preProcessXml(root);//从Document的根元素开始进行Bean定义的Document对象parseBeanDefinitions(root, this.delegate);//在解析Bean定义之后,进行自定义的解析,增加解析过程的可扩展性postProcessXml(root);this.delegate = parent;
}
//创建BeanDefinitionParserDelegate,用于完成真正的解析过程
protected BeanDefinitionParserDelegate createDelegate(XmlReaderContext readerContext, Element root, @Nullable BeanDefinitionParserDelegate parentDelegate) {BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext);//BeanDefinitionParserDelegate初始化Document根元素delegate.initDefaults(root, parentDelegate);return delegate;
}
//使用Spring的Bean规则从Document的根元素开始进行Bean定义的Document对象
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {//Bean定义的Document对象使用了Spring默认的XML命名空间if (delegate.isDefaultNamespace(root)) {//获取Bean定义的Document对象根元素的所有子节点NodeList nl = root.getChildNodes();for (int i = 0; i < nl.getLength(); i++) {Node node = nl.item(i);//获得Document节点是XML元素节点if (node instanceof Element) {Element ele = (Element) node;//Bean定义的Document的元素节点使用的是Spring默认的XML命名空间if (delegate.isDefaultNamespace(ele)) {//使用Spring的Bean规则解析元素节点parseDefaultElement(ele, delegate);}else {//没有使用Spring默认的XML命名空间,则使用用户自定义的解//析规则解析元素节点delegate.parseCustomElement(ele);}}}}else {//Document的根节点没有使用Spring默认的命名空间,则使用用户自定义的//解析规则解析Document根节点delegate.parseCustomElement(root);}
}
//使用Spring的Bean规则解析Document元素节点
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>元素,//按照Spring的Bean规则解析元素else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {processBeanDefinition(ele, delegate);}else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {// recursedoRegisterBeanDefinitions(ele);}
}//解析<Import>导入元素,从给定的导入路径加载Bean定义资源到Spring IoC容器中
protected void importBeanDefinitionResource(Element ele) {//获取给定的导入元素的location属性String location = ele.getAttribute(RESOURCE_ATTRIBUTE);//如果导入元素的location属性值为空,则没有导入任何资源,直接返回if (!StringUtils.hasText(location)) {getReaderContext().error("Resource location must not be empty", ele);return;}// Resolve system properties: e.g. "${user.dir}"//使用系统变量值解析location属性值location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);Set<Resource> actualResources = new LinkedHashSet<>(4);// Discover whether the location is an absolute or relative URI//标识给定的导入元素的location是否是绝对路径boolean absoluteLocation = false;try {absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();}catch (URISyntaxException ex) {// cannot convert to an URI, considering the location relative// unless it is the well-known Spring prefix "classpath*:"//给定的导入元素的location不是绝对路径}// Absolute or relative?//给定的导入元素的location是绝对路径if (absoluteLocation) {try {//使用资源读入器加载给定路径的Bean定义资源int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);if (logger.isDebugEnabled()) {logger.debug("Imported " + importCount + " bean definitions from URL location [" + location + "]");}}catch (BeanDefinitionStoreException ex) {getReaderContext().error("Failed to import bean definitions from URL location [" + location + "]", ele, ex);}}else {// No URL -> considering resource location as relative to the current file.//给定的导入元素的location是相对路径try {int importCount;//将给定导入元素的location封装为相对路径资源Resource relativeResource = getReaderContext().getResource().createRelative(location);//封装的相对路径资源存在if (relativeResource.exists()) {//使用资源读入器加载Bean定义资源importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);actualResources.add(relativeResource);}//封装的相对路径资源不存在else {//获取Spring IOC容器资源读入器的基本路径String baseLocation = getReaderContext().getResource().getURL().toString();//根据Spring IOC容器资源读入器的基本路径加载给定导入路径的资源importCount = getReaderContext().getReader().loadBeanDefinitions(StringUtils.applyRelativePath(baseLocation, location), actualResources);}if (logger.isDebugEnabled()) {logger.debug("Imported " + importCount + " bean definitions from relative location [" + location + "]");}}catch (IOException ex) {getReaderContext().error("Failed to resolve current resource location", ele, ex);}catch (BeanDefinitionStoreException ex) {getReaderContext().error("Failed to import bean definitions from relative location [" + location + "]",ele, ex);}}Resource[] actResArray = actualResources.toArray(new Resource[actualResources.size()]);//在解析完<Import>元素之后,发送容器导入其他资源处理完成事件getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
}/*** Process the given alias element, registering the alias with the registry.*/
//解析<Alias>别名元素,为Bean向Spring IoC容器注册别名
protected void processAliasRegistration(Element ele) {//获取<Alias>别名元素中name的属性值String name = ele.getAttribute(NAME_ATTRIBUTE);//获取<Alias>别名元素中alias的属性值String alias = ele.getAttribute(ALIAS_ATTRIBUTE);boolean valid = true;//<alias>别名元素的name属性值为空if (!StringUtils.hasText(name)) {getReaderContext().error("Name must not be empty", ele);valid = false;}//<alias>别名元素的alias属性值为空if (!StringUtils.hasText(alias)) {getReaderContext().error("Alias must not be empty", ele);valid = false;}if (valid) {try {//向容器的资源读入器注册别名getReaderContext().getRegistry().registerAlias(name, alias);}catch (Exception ex) {getReaderContext().error("Failed to register alias '" + alias +"' for bean with name '" + name + "'", ele, ex);}//在解析完<Alias>元素之后,发送容器别名处理完成事件getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));}
}/*** Process the given bean element, parsing the bean definition* and registering it with the registry.*/
//解析Bean定义资源Document对象的普通元素
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);// BeanDefinitionHolder是对BeanDefinition的封装,即Bean定义的封装类//对Document对象中<Bean>元素的解析由BeanDefinitionParserDelegate实现// BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);if (bdHolder != null) {bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);try {// Register the final decorated instance.//向Spring IOC容器注册解析得到的Bean定义,这是Bean定义向IOC容器注册的入口BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());}catch (BeanDefinitionStoreException ex) {getReaderContext().error("Failed to register bean definition with name '" +bdHolder.getBeanName() + "'", ele, ex);}// Send registration event.//在完成向Spring IOC容器注册解析得到的Bean定义之后,发送注册事件getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));}
}

通过上述Spring IOC 容器对载入的Bean 定义Document 解析可以看出,我们使用Spring 时,在Spring 配置文件中可以使用<import>元素来导入IOC 容器所需要的其他资源,Spring IOC 容器在解析时会首先将指定导入的资源加载进容器中。使用<ailas>别名时,Spring IOC 容器首先将别名元素所定义的别名注册到容器中。

对于既不是<import>元素,又不是<alias>元素的元素,即Spring 配置文件中普通的<bean>元素的解析由BeanDefinitionParserDelegate 类的parseBeanDefinitionElement()方法来实现。这个解析的过程非常复杂,我们在mini 版本的时候,就用properties 文件代替了。

基于Xml 的IOC 容器-将配置载入内存相关推荐

  1. 基于Xml 的IOC 容器-获得配置路径

    通过分析ClassPathXmlApplicationContext 的源代码可以知道, 在创建ClassPathXmlApplicationContext 容器时,构造方法做以下两项重要工作: 首先 ...

  2. 一步一步手绘Spring IOC运行时序图二(基于XML的IOC容器初始化)

    相关内容: 架构师系列内容:架构师学习笔记(持续更新) 一步一步手绘Spring IOC运行时序图一(Spring 核心容器 IOC初始化过程) 一步一步手绘Spring IOC运行时序图二(基于XM ...

  3. 从源码深处体验Spring核心技术--基于Xml的IOC容器的初始化

    IOC 容器的初始化包括 BeanDefinition 的 Resource 定位.加载和注册这三个基本的过程. 我们以ApplicationContext 为例讲解,ApplicationConte ...

  4. 基于Xml 的IOC 容器-载入<bean>元素

    Bean 配置信息中的<import>和<alias>元素解析在DefaultBeanDefinitionDocumentReader 中已经完成,对Bean 配置信息中使用最 ...

  5. 基于Xml 的IOC 容器-载入<list>的子元素

    在BeanDefinitionParserDelegate 类中的parseListElement()方法就是具体实现解析<property>元素中的<list>集合子元素,源 ...

  6. 基于Xml 的IOC 容器-分配解析策略

    XmlBeanDefinitionReader 类中的doLoadBeanDefinition()方法是从特定XML 文件中实际载入Bean 配置资源的方法,该方法在载入Bean 配置资源之后将其转换 ...

  7. 基于Xml 的IOC 容器-准备文档对象

    DocumentLoader 将Bean 配置资源转换成Document 对象的源码如下: //使用标准的JAXP将载入的Bean定义资源转换成document对象 @Override public ...

  8. 基于Xml 的IOC 容器-创建容器

    obtainFreshBeanFactory()方法调用子类容器的refreshBeanFactory()方法,启动容器载入Bean 配置信息的过程,代码如下: protected Configura ...

  9. 基于Xml 的IOC 容器-开始启动

    SpringIOC 容器对Bean 配置资源的载入是从refresh()函数开始的,refresh()是一个模板方法,规定了IOC 容器的启动流程, 有些逻辑要交给其子类去实现.它对Bean 配置资源 ...

最新文章

  1. hadoop28---netty传对象
  2. java 读取中文配置文件问题
  3. jekyll网站上传服务器,jekyll 高效搭建个人博客之完整流程
  4. 在java.library.path中找不到允许在生产环境中实现最佳性能的基于APR的Apache Tom.....
  5. python程序设计题答案_《Python程序设计》习题与答案
  6. 这篇Nature子刊文章的蛋白组学数据PCA分析竟花费了我两天时间来重现|附全过程代码...
  7. mysql炸包_炸裂!MySQL 82 张图带你飞
  8. 联想童夫尧:细分市场将是PC未来新增长点
  9. 在面向服务的设计时有四个原则:
  10. 中文同义词近义词库 vs 词向量
  11. 计算机软考知识点总结,系统分析师历年计算机软考复习知识点总结(10)
  12. 大数据算法_大数据挖掘十大经典算法
  13. 激光雷达的障碍物检测
  14. 随机指标计算机程序,MACD/随机指标组合应用分析
  15. 九九乘法c语言的编程,C语言九九乘法表
  16. geany菜单怎么改语言
  17. 如何做番茄炖牛腩——hadoop理解
  18. MCS-51单片机内部结构——CPU结构 单片机原理学习笔记(二)
  19. 我国AIS信息服务平台上线 东方通大数据处理亿级报文
  20. 利用Cookie显示上次登录时间

热门文章

  1. 阅读之spring+Dubbo
  2. tomcat错误:The page you tried to access (/manager/login.do) does not exist
  3. Scala中的Map使用例子
  4. Linux下同步网络时间
  5. HashMap是如何实现快速存取的
  6. 《Android编程权威指南》PhotoGallery应用梳理
  7. OC-通知+Block
  8. ICSharpCode.SharpZipLib.dll,MyZip.dll,Ionic.Zip.dll 使用
  9. PBOC中文件结构,文件类型解析
  10. Android的Notification研究