原文出自: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. JavaEE Log4j

    1.Log4j是Apache的一个开放源代码项目,可以通过一个配置文件来灵活的进行配置,而不需要修改应用的代码. 2.Log4j有三个主要部件组成: 记录器:按照布局中指定的格式把日志信息写入一个或多 ...

  2. dedecms /install/index.php.bak Installation File Not Deleted &amp;&amp; Executed Via Apache Analytic Vul

    catalog . 漏洞描述 . 漏洞触发条件 . 漏洞影响范围 . 漏洞代码分析 . 防御方法 . 攻防思考 1. 漏洞描述 概括梳理一下这个漏洞的成因 . dedecms默认情况下安装完成之后,i ...

  3. git在本地创建远程仓库

    类似的博文,在前面的帖子里面也提到过,当时讲述的是一个入门级别的.其URL是ssh://username@repo-host-address/repo-path这种格式. 今天再说说如何创建类似Git ...

  4. How Tomcat Works(十三)

    本文分析tomcat容器的安全管理,servlet技术支持通过配置部署描述器(web.xml文件)来对受限内容进行访问控制:servlet容器是通过一个名为验证器的阀来支持安全限制的,当servlet ...

  5. vmware9.0 安装ios10.8应该注意的地方

    今天终于在我的thinkpad t400上面按照好了ios系统 我的硬件配置:cpu:p8700,内存:ddr3,6g 安装的版本:ios10.8 vmware的版本是vmware9.0 安装好的io ...

  6. System Operations on AWS - Lab 7 - CloudFormation

    CloudFormation模板:创建一个VPC(包含Public子网,Private子网,分别在不同的AZ),创建NAT,Bastion Server在Public子网. 1. 修改并运行AWS C ...

  7. express学习点滴- methodOverride

    app.use(express.methodOverride());在app.js中可以看到这个中间件,去翻文档,几乎没有提到.一番寻找,整理如下. 作用是使用REST风格的http请求时,进行改写后 ...

  8. 菲菲更名宝贝(批量更名软件) v8.0 绿色版

    软件名称: 菲菲更名宝贝(批量更名软件)软件语言: 简体中文授权方式: 免费软件运行环境: Win7 / Vista / Win2003 / WinXP 软件大小: 1.5MB图片预览: 软件简介:菲 ...

  9. 二级横向菜单实现——ListView

    实现类似于大众点评客户端的横向listview二级列表 这种横向的listview二级列表在手机软件上还不太常见,但是使用过平板的都应该知道,在平板上市比较常见的.可能是因为平板屏幕比较大,而且也能展 ...

  10. HALCON学习-资料

    HALCON学习网: http://www.ihalcon.com/ 学习资料推荐博客: http://k594081130.blog.163.com/blog/static/218359013201 ...