控制反转(Inversion of Control,英文缩写为IoC)是一个重要的面向对象编程的法则来削减计算机程序的耦合问题,也是轻量级的Spring框架的核心。

控制反转一般分为两种类型,依赖注入(Dependency Injection,简称DI)和依赖查找。依赖注入应用比较广泛。

将控制权从具体的对象手中交给平台或者是框架。

  • BeanFactory是基本的功能接口
public interface BeanFactory {

    //这里是对FactoryBean的转义定义,因为如果使用bean的名字检索FactoryBean得到的对象是工厂生成的对象,
//如果需要得到工厂本身,需要转义
String FACTORY_BEAN_PREFIX = "&"; //这里根据bean的名字,在IOC容器中得到bean实例,这个IOC容器就是一个大的抽象工厂。
Object getBean(String name) throws BeansException; //这里根据bean的名字和Class类型来得到bean实例,和上面的方法不同在于它会抛出异常:如果根据名字取得的bean实例的Class类型和需要的不同的话。
Object getBean(String name, Class requiredType) throws BeansException; //这里提供对bean的检索,看看是否在IOC容器有这个名字的bean
boolean containsBean(String name); //这里根据bean名字得到bean实例,并同时判断这个bean是不是单件
boolean isSingleton(String name) throws NoSuchBeanDefinitionException; //这里对得到bean实例的Class类型
Class getType(String name) throws NoSuchBeanDefinitionException; //这里得到bean的别名,如果根据别名检索,那么其原名也会被检索出来
String[] getAliases(String name); }

BeanFactory只是对IOC容器中的基本行为作了定义,但是并没有管理如何加载 baen.XmlBeanFactory和ApplicationContext。

Spring提供了一个BeanFactory的基本实现----xmlBaenFactory.AbstractBeanFactory,DefaultListableBeanFactory这些抽象类为其提供模板服务.

Resource接口来抽象Bean的数据对XML定义文件的解释委托给XmlBeanDefinitionReader来完成。

  • XmlBeanFacotry的使用过程
  1. 创建IOC配置文件的抽象资源
  2. 创建一个BeanFactory
  3. 把XmlBeanDefinitionReader配置给BeanFactory
  4. XmlBeanDefinitionReader完成资源的解释,完成对IOC容器的加载
  • XmlBeanFactory的源代码
  • public class XmlBeanFactory extends DefaultListableBeanFactory {
    //这里为容器定义了一个默认使用的bean定义读取器
    private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
    public XmlBeanFactory(Resource resource) throws BeansException {
    this(resource, null);
    }
    //在初始化函数中使用读取器来对资源进行读取,得到bean定义信息。
    public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
    super(parentBeanFactory);
    this.reader.loadBeanDefinitions(resource);
    }

XmlBeanFactoy和ApplicationContext的区别,XmlBeanFactory不具备定义资源的能力,

  • ApplicationContext

继承Messagesource,可以支持不同的信息源

访问资源,ResourceLoader和Resource,可以从不同的地方来获取Bean的定义资源

支持应用事件,继承了ApplicationEvnetPublisher接口

ApplicationContext允许上下文嵌套 - 通过保持父上下文可以维持一个上下文体系 - 这个体系我们在以后对Web容器中的上下文环境的分析中可以清楚地看到。

对于bean的查找可以在这个上下文体系中发生,首先检查当前上下文,其次是父上下文,逐级向上,这样为不同的Spring应用提供了一个共享的bean定义环境。

这个我们在分析Web容器中的上下文环境时也能看到。

ApplicationContext提供IoC容器的主要接口,在其体系中有许多抽象子类比如AbstractApplicationContext为具体的BeanFactory的实现。

比如FileSystemXmlApplicationContext和 ClassPathXmlApplicationContext提供上下文的模板,使得他们只需要关心具体的资源定位问题。

当应用程序代码实例化 FileSystemXmlApplicationContext的时候,得到IoC容器的一种具体表现 - ApplicationContext,从而应用程序通过ApplicationContext来管理对bean的操作。

BeanFactory 是一个接口,在实际应用中我们一般使用ApplicationContext来使用IOC容器,

它们也是IOC容器展现给应用开发者的使用接口。对应用程序开发者来说,可以认为BeanFactory和ApplicationFactory在不同的使用层面上代表了SPRING提供的IOC容器服务。

  • FileSystemXmlApplicationContext简历IOC容器

Refresh的模板在AbstractApplicationContext:refresh的模板在AbstractApplicationContext:refresh的模板在AbstractApplicationContext

这个方法包含了整个BeanFactory初始化的过程,对于特定的FileSystemXmlBeanFactory,我们看到定位资源位置由refreshBeanFactory()来实现

在AbstractXmlApplicationContext中定义了对资源的读取过程,默认由XmlBeanDefinitionReader来读取:

  • 转到beanDefinitionReader中进行处理
    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources();
if (configResources != null) {
//调用XmlBeanDefinitionReader来载入bean定义信息。
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
  • 转到beanDefinitionReader中进行处理
  • 而在作为其抽象父类的AbstractBeanDefinitionReader中来定义载入过程
  • 当我们通过ResourceLoader来载入资源,别忘了了我们的GenericApplicationContext也实现了ResourceLoader接口
    public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {
public Resource getResource(String location) {
//这里调用当前的loader也就是DefaultResourceLoader来完成载入
if (this.resourceLoader != null) {
return this.resourceLoader.getResource(location);
}
return super.getResource(location);
}
.......
}

而我们的FileSystemXmlApplicationContext就是一个DefaultResourceLoader - GenericApplicationContext()通过DefaultResourceLoader

    public Resource getResource(String location) {
//如果是类路径的方式,那需要使用ClassPathResource来得到bean文件的资源对象
if (location.startsWith(CLASSPATH_URL_PREFIX)) {
return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
}
else {
try {
// 如果是URL方式,使用UrlResource作为bean文件的资源对象
URL url = new URL(location);
return new UrlResource(url);
}
catch (MalformedURLException ex) {
// 如果都不是,那我们只能委托给子类由子类来决定使用什么样的资源对象了
return getResourceByPath(location);
}
}
}

我们的FileSystemXmlApplicationContext本身就是是DefaultResourceLoader的实现类,实现了以下的接口

 protected Resource getResourceByPath(String path) {
if (path != null && path.startsWith("/")) {
path = path.substring(1);
}
//这里使用文件系统资源对象来定义bean文件
return new FileSystemResource(path);

这样代码就回到了FileSystemXmlApplicationContext中来,他提供了FileSystemResource来完成从文件系统得到配置文件的资源定义。

这样就可以从文件系统路径上对IOC配置文件进行加载 - 当然我们可以按照这个逻辑从任何地方加载,在Spring中我们看到它提供的各种资源抽象,比如ClassPathResource, URLResource,FileSystemResource等来供我们使用。

上面我们看到的是定位Resource的一个过程,而这只是加载过程的一部分 。

回到AbstractBeanDefinitionReaderz中的loadDefinitions(resource)来看看得到代表bean文件的资源定义以后的载入过程,默认的我们使用XmlBeanDefinitionReader

    public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
.......
try {
//这里通过Resource得到InputStream的IO流
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
//从InputStream中得到XML的解析源
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//这里是具体的解析和注册过程
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
//关闭从Resource中得到的IO流
inputStream.close();
}
}
} protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
int validationMode = getValidationModeForResource(resource);
//通过解析得到DOM,然后完成bean在IOC容器中的注册
Document doc = this.documentLoader.loadDocument(
inputSource, this.entityResolver, this.errorHandler, validationMode, this.namespaceAware);
return registerBeanDefinitions(doc, resource);
}
} protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
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);
}
}

Spring IOC 源码浅析的更多相关文章

  1. spring初始化源码浅析之关键类和扩展接口

    目录 1.关键接口和类 1.1.关键类之 DefaultListableBeanFactory 1.2.关键类之XmlBeanDefinitionReader 1.3.关键类之ClassPathXml ...

  2. 深入Spring IOC源码之ResourceLoader

    在<深入Spring IOC源码之Resource>中已经详细介绍了Spring中Resource的抽象,Resource接口有很多实现类,我们当然可以使用各自的构造函数创建符合需求的Re ...

  3. Spring IOC 源码之ResourceLoader

    转载自http://www.blogjava.net/DLevin/archive/2012/12/01/392337.html 在<深入Spring IOC源码之Resource>中已经 ...

  4. Spring IOC 源码分析

    Spring 最重要的概念是 IOC 和 AOP,本篇文章其实就是要带领大家来分析下 Spring 的 IOC 容器.既然大家平时都要用到 Spring,怎么可以不好好了解 Spring 呢?阅读本文 ...

  5. spring IoC源码分析 (3)Resource解析

    引自 spring IoC源码分析 (3)Resource解析 定义好了Resource之后,看到XmlFactoryBean的构造函数 public XmlBeanFactory(Resource  ...

  6. Spring IoC源码解析之invokeBeanFactoryPostProcessors

    一.Bean工厂的后置处理器 Bean工厂的后置处理器:BeanFactoryPostProcessor(触发时机:bean定义注册之后bean实例化之前)和BeanDefinitionRegistr ...

  7. Spring IoC源码解析之getBean

    一.实例化所有的非懒加载的单实例Bean 从org.springframework.context.support.AbstractApplicationContext#refresh方法开发,进入到 ...

  8. Spring系列(三):Spring IoC源码解析

    一.Spring容器类继承图 二.容器前期准备 IoC源码解析入口: /** * @desc: ioc原理解析 启动 * @author: toby * @date: 2019/7/22 22:20 ...

  9. Spring IoC 源码分析 (基于注解) 之 包扫描

    在上篇文章Spring IoC 源码分析 (基于注解) 一我们分析到,我们通过AnnotationConfigApplicationContext类传入一个包路径启动Spring之后,会首先初始化包扫 ...

随机推荐

  1. 华为S5700S-52P-LI-AC千兆网管交换机web登录界面配置

    研究一下午,包装附的说明书根本就是错误的,通过技术售后和官方的文档结合,总算可以登录交换机的web管理界面. 首先需要使用通讯控制线缆(包装中附)连接电脑和交换机,一头接交换机的Console口,一头 ...

  2. 一条诡异的insert语句

    问题背景 有同事反馈在mysql上面执行一条普通的insert语句,结果报错, execute failed due to >>> Incorrect string value: ' ...

  3. Storm系列(二):使用Csharp创建你的第一个Storm拓扑(wordcount)

    WordCount在大数据领域就像学习一门语言时的hello world,得益于Storm的开源以及Storm.Net.Adapter,现在我们也可以像Java或Python一样,使用Csharp创建 ...

  4. 一个有趣的SQL Server 层级汇总数据问题

        看SQL Server大V宋大侠的博客文章,发现了一个有趣的sql server层级汇总数据问题.          具体的问题如下:     parent_id emp_id emp_nam ...

  5. Windows下常用软件工具的命令

    Linux上主要操作是命令,懂一点linux知识的都知道,其实windows下边很多工具也是可以用命令来操作打开的,这样会提高效率,节省很多的时间.下边就记录一下常用的命令. 一.常用命令 1.远程桌 ...

  6. android java数组应用与说明

    如果定义类或结构数据则需每个都进行创建才可以使用,不然都为null 如: PointF pts[] = new PointF[5] ;//pts[0].x =CSSliderSize / 2;//这样 ...

  7. Android学习笔记之AndroidManifest.xml文件解析(转)

    //自已备注: <?xml version="1.0" encoding="utf-8"?>//说明了版本号,字符集 <manifest xm ...

  8. Flash Download Failed-"Cortex-M3"

    rror:Flash Download Failed-"Cortex-M3"出现一般有两种情况: 1.SWD模式下,Debug菜单中,Reset菜单选项(Autodetect/HW ...

  9. Team Foundation Server 15 功能初探

    1. 系统安装 1.1. 系统需求 新版的TFS的系统要求发生了很大的变化,主要包含: - 不再支持32位的操作系统,只支持64位操作系统 - 只支持SQL 2014和SQL Server 2016, ...

  10. AI(Adobe Illustrator)简单入门——米老鼠

    成果: 步骤如下: 一.新建文档 二.选椭圆工具,在画布中间点一下,画一个100px*100px的圆,如下 三.同上,再画两个50px*50px小圆.点左上角的选择工具,点小圆中心,放好位置. 四.全 ...