控制反转(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. CMPP错误码说明

    与中国移动代码的对应关系. MI::zzzzSMSC返回状态报告的状态值为EXPIREDMJ:zzzzSMSC返回状态报告的状态值为DELETEDMK:zzzzSMSC返回状态报告的状态值为UNDEL ...

  2. jq+css+html简单实现导航下拉菜单

    相信导航栏下拉菜单是web开发最常见的一个item了.这里就不做介绍了,直接上code. Html部分 <div class="_nav"> <ul id=&qu ...

  3. Java 使用 JRegistry-1.8.1 读取和设置 windows 注册表

    在一个监控相关的Java项目中,需要读取windows系统的注册表,搜索到使用 JRegistery 可以解决.代码如下: /** * @author digdeep@126.com */ publi ...

  4. 绕过校园网的共享限制 win10搭建VPN服务器实现--从入门到放弃

    一.开篇立论= =.. 上次说到博主在电脑上搭建了代理服务器来绕过天翼客户端的共享限制,然而经过实际测试还不够完美,所以本着生命不息,折腾不止的精神,我又开始研究搭建vpn服务器= =... (上次的 ...

  5. ASP.NET Core 1.0 安装并发布到Centos 7.2 使用jexus 5.8.2

    安装运行环境 sudoyuminstall libunwind libicu 下载.net core https://www.microsoft.com/net/download 下载完后上传文件 安 ...

  6. Java基础语法总结1

    一.标识符及字符集 Java语言规定标识符是以字母.下划线"_"或美元符号"$"开始,随后可跟数字.字母.下划线或美元符号的字符序列.Java标识符大小写敏感, ...

  7. android 反编译apktool工具

    下载地址:http://pan.baidu.com/s/1bnHANtd 1.将编译的*.apk放在apktool的根目录下:2.双击“解压软件.bat”后,会提示完成:这样就反编译成功以:3.查看反 ...

  8. linux开机自动连接无线网络

           1.右击无线网络图标的“编辑连接”. 2.在“无线”选项卡里,选择“编辑”. 3.在“无线安全性”选项卡里,输入无线密匙,并选中左下角的“对所有用户可      用”的选项点击应用,会提 ...

  9. 关于JavaScript继承的那些事

    在JavaScript中,对象的创建可以脱离类型(class free),通过字面量的方式可以很方便的创建出自定义对象. 另外,JavaScript中拥有原型这个强大的概念,当对象进行属性查找的时候, ...

  10. AI (Adobe Illustrator)详细用法(四)

    本节主要是介绍和形状相关的操作. 一.外观面板的使用 熟悉外观面板的使用很重要. 1.新增描边 外观面板可以让我们增加多个描边. 点击“新增描边”,系统自动添加一个描边. 选中文字,新增描边,可以修改 ...