原文出自:http://cmsblogs.com

先看一段熟悉的代码:

ClassPathResource resource = new ClassPathResource("bean.xml");
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(resource);

这段代码是 Spring 中编程式使用 IOC 容器,通过这四段简单的代码,我们可以初步判断 IOC 容器的使用过程。

  • 获取资源
  • 获取 BeanFactory
  • 根据新建的 BeanFactory 创建一个BeanDefinitionReader对象,该Reader 对象为资源的解析器
  • 装载资源

整个过程就分为三个步骤:资源定位、装载、注册,如下:

  • 资源定位。我们一般用外部资源来描述 Bean 对象,所以在初始化 IOC 容器的第一步就是需要定位这个外部资源。在上一篇博客(【死磕 Spring】----- IOC 之 Spring 统一资源加载策略)已经详细说明了资源加载的过程。
  • 装载。装载就是 BeanDefinition 的载入。BeanDefinitionReader 读取、解析 Resource 资源,也就是将用户定义的 Bean 表示成 IOC 容器的内部数据结构:BeanDefinition。在 IOC 容器内部维护着一个 BeanDefinition Map 的数据结构,在配置文件中每一个 <bean> 都对应着一个BeanDefinition对象。
  • 注册。向IOC容器注册在第二步解析好的 BeanDefinition,这个过程是通过 BeanDefinitionRegistery 接口来实现的。在 IOC 容器内部其实是将第二个过程解析得到的 BeanDefinition 注入到一个 HashMap 容器中,IOC 容器就是通过这个 HashMap 来维护这些 BeanDefinition 的。在这里需要注意的一点是这个过程并没有完成依赖注入,依赖注册是发生在应用第一次调用 getBean() 向容器索要 Bean 时。当然我们可以通过设置预处理,即对某个 Bean 设置 lazyinit 属性,那么这个 Bean 的依赖注入就会在容器初始化的时候完成。

资源定位在前面已经分析了,下面我们直接分析加载,上面提过 reader.loadBeanDefinitions(resource) 才是加载资源的真正实现,所以我们直接从该方法入手。

	public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}

从指定的 xml 文件加载 Bean Definition,这里会先对 Resource 资源封装成 EncodedResource。这里为什么需要将 Resource 封装成 EncodedResource呢?主要是为了对 Resource 进行编码,保证内容读取的正确性。封装成 EncodedResource 后,调用 loadBeanDefinitions(),这个方法才是真正的逻辑实现。如下:

    public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isInfoEnabled()) {
logger.info("Loading XML bean definitions from " + encodedResource.getResource());
} // 获取已经加载过的资源
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
} // 将当前资源加入记录中
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
// 从 EncodedResource 获取封装的 Resource 并从 Resource 中获取其中的 InputStream
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
// 设置编码
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
// 核心逻辑部分
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
inputStream.close();
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
// 从缓存中剔除该资源
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}

首先通过 resourcesCurrentlyBeingLoaded.get() 来获取已经加载过的资源,然后将 encodedResource 加入其中,如果 resourcesCurrentlyBeingLoaded 中已经存在该资源,则抛出 BeanDefinitionStoreException 异常。完成后从 encodedResource 获取封装的 Resource 资源并从 Resource 中获取相应的 InputStream ,最后将 InputStream 封装为 InputSource 调用 doLoadBeanDefinitions()。方法 doLoadBeanDefinitions() 为从 xml 文件中加载 Bean Definition 的真正逻辑,如下:

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
// 获取 Document 实例
Document doc = doLoadDocument(inputSource, resource);
// 根据 Document 实例****注册 Bean信息
return registerBeanDefinitions(doc, resource);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (SAXParseException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
}
catch (SAXException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"XML document from " + resource + " is invalid", ex);
}
catch (ParserConfigurationException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Parser configuration exception parsing XML from " + resource, ex);
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"IOException parsing XML document from " + resource, ex);
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Unexpected exception parsing XML document from " + resource, ex);
}
}

核心部分就是 try 块的两行代码。

  1. 调用 doLoadDocument() 方法,根据 xml 文件获取 Document 实例。
  2. 根据获取的 Document 实例注册 Bean 信息。

其实在 doLoadDocument()方法内部还获取了 xml 文件的验证模式。如下:

	protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
}

调用getValidationModeForResource() 获取指定资源(xml)的验证模式。所以 doLoadBeanDefinitions()主要就是做了三件事情。

  1. 调用 getValidationModeForResource() 获取 xml 文件的验证模式
  2. 调用 loadDocument() 根据 xml 文件获取相应的 Document 实例。
  3. 调用 registerBeanDefinitions() 注册 Bean 实例。

【死磕 Spring】----- IOC 之 加载 Bean的更多相关文章

  1. IoC 之加载 Bean:总结

    上文中我们将bean已经加载到了IOC容器中,接下来我们将把IOC加载Bean出来进行代码解析 备注:(有些解释是参考别个博客的相关解释 )一起探讨请加我QQ:1051980588 bean 的初始化 ...

  2. Dubbo死磕之扩展点加载ExetnsionLoader

    dubbo的SPI机制与JDK的SPI机制对比        dubbo一款阿里一款开源的RPC框架,他本身是一款非常复杂的系统,我们主要针对里边的一些核心点来展开分析,其中duboo里的一种核心机制 ...

  3. Spring源码:Spring IoC容器加载过程(2)

    Spring源码版本:4.3.23.RELEASE 一.加载XML配置 通过XML配置创建Spring,创建入口是使用org.springframework.context.support.Class ...

  4. Spring源码:Spring IoC容器加载过程(1)

    Spring源码版本:4.3.23.RELEASE 一.加载过程概览 Spring容器加载过程可以在org.springframework.context.support.AbstractApplic ...

  5. 梳理源码:spring ioc容器加载的流程图

  6. 【死磕 Spring】----- IOC 之 获取验证模型

    原文出自:http://cmsblogs.com 在上篇博客[死磕Spring]----- IOC 之 加载 Bean 中提到,在核心逻辑方法 doLoadBeanDefinitions()中主要是做 ...

  7. spring加载bean流程解析

    spring作为目前我们开发的基础框架,每天的开发工作基本和他形影不离,作为管理bean的最经典.优秀的框架,它的复杂程度往往令人望而却步.不过作为朝夕相处的框架,我们必须得明白一个问题就是sprin ...

  8. Spring IOC容器分析(4) -- bean创建获取完整流程

    上节探讨了Spring IOC容器中getBean方法,下面我们将自行编写测试用例,深入跟踪分析bean对象创建过程. 测试环境创建 测试示例代码如下: package org.springframe ...

  9. Bean XML 配置(1)- 通过XML配置加载Bean

    系列教程 Spring 框架介绍 Spring 框架模块 Spring开发环境搭建(Eclipse) 创建一个简单的Spring应用 Spring 控制反转容器(Inversion of Contro ...

随机推荐

  1. aliyun oss操作汇总

    // endpoint以杭州为例,其它region请按实际情况填写 String endpoint = "http://oss-cn-hangzhou.aliyuncs.com"; ...

  2. CSS3 :nth-child() 选择器---挖坑

    E:nth-child(n) 语法: E:nth-child(n) { sRules } 说明: 匹配父元素的第n个子元素E,假设该子元素不是E,则选择符无效.(也就是说,会检查从body开始的每个元 ...

  3. 整合 ucenter 注册自动激活

    http://my.oschina.net/banbo/blog/311691 应用整合 UCenter,同步注册到 Discuz 的用户,在 Discuz 登录时得手动激活,用户体验很不好,不过解决 ...

  4. ThreadPoolExecutor简介

    ThreadPoolExecutor简介 并发包中提供的一个线程池服务 23456789 public ThreadPoolExecutor(int corePoolSize,//线程池维护线程的最少 ...

  5. Swift 结构体的使用

    Swift 结构体是构建代码所用的一种通用且灵活的构造体. 我们可以为结构体定义属性(常量.变量)和添加方法,从而扩展结构体的功能. 与 C 和 Objective C 不同的是: 结构体不需要包含实 ...

  6. php与html实现交互的基本操作

    今天我们来实现php与html页面注册和登录的效果.中国有句古话叫: 第一步:我们来了解一些php的基本格式. <?php php代码 ?> 第二步:了解php与js的一些基本区别 我们在 ...

  7. VMware workstation的基础使用

    1. VMware workstation虚拟化平台简介2. VMware workstation提供网络资源3. VMware workstation提供存储资源4. VMware workstat ...

  8. 大白话5分钟带你走进人工智能-第十五节L1和L2正则几何解释和Ridge,Lasso,Elastic Net回归

    第十五节L1和L2正则几何解释和Ridge,Lasso,Elastic Net回归 上一节中我们讲解了L1和L2正则的概念,知道了L1和L2都会使不重要的维度权重下降得多,重要的维度权重下降得少,引入 ...

  9. 从壹开始 [ Nuxt.js ] 之一 || 为开源收录Bug之 TiBug项目 开篇讲

    缘起 哈喽大家周二好呀,刚刚经历过了几天火车抢票,整个人都不好了,不知道小伙伴对今年的春节是否还一如既往的期待呢,眼看都要春节了,本来也想写篇2018总结篇,但是怕不免会出现鸡汤文的窠臼嫌疑,想想还是 ...

  10. Logistic回归(逻辑回归)和softmax回归

    一.Logistic回归 Logistic回归(Logistic Regression,简称LR)是一种常用的处理二类分类问题的模型. 在二类分类问题中,把因变量y可能属于的两个类分别称为负类和正类, ...