一、IOC 到底为何物?
IOC(
Inversion of Control) ,直译过来叫控制反转; 其实是在面向对象编程的一种设计原则,可以用来降低计算机代码之间的耦合度;
其中最常见的方式叫做依赖注入(Dependency Injection 简称DI);
维基百科中关于其来源
:在2004年Martin Fowler就提出了“哪些方面的控制被反转了?”这个问题。他总结出是依赖对象的获得被反转了,因为大多数应用程序都是由两个或是更多的类通过彼此的合作来实现业务逻辑,这使得每个对象都需要获取与其合作的对象(也就是它所依赖的对象)的引用。如果这个获取过程要靠自身实现,那么这将导致代码高度耦合并且难以维护和调试。
其实对IOC容器的使用者来说,我们经常接触到的BeanFactory和ApplicationContext 都可以看成是容器的具体表现形式。如果深入到Spring的实现中去看,就会看到其实IOC容器是一系列的产品;就像我们需要水桶,但是水桶分很多类,有铁桶,有木桶,有塑料桶,但是无论是什么样的桶,只要满足桶的基本要求:能装水,那他就是一个桶(容器)。但是具体使用什么样的桶,那就要根据客户自己的需求来判断了。
在这些Spring提供的基本IOC 容器的接口定义和实现的基础上,Spring 通过定义BeanDefinition来管理基于Spring的应用中的各种对象以及它们之间的相互依赖关系。BeanDefinition抽象了我们对Bean的定义,是让容器起作用的主要数据类型。我们都知道,在计算机世界里,所有的功能都是建立在通过对现实进行抽象的基础上的。IOC 容器是用来管理对象依赖关系的,对IOC 容器来说,BeanDefinition就是对依赖反转模式中管理的对象依赖关系的数据抽象,也是容器实现依赖反转功能的核心数据结构,依赖反转功能都是围绕对这个BeanDefinition 的处理来完成的。这些BeanDefinition 就像是容器里装的水,有了这些基本数据,容器才能够发挥作用。
注解:在项目中需要很多类需要一起工作来完成一个业务逻辑功能,那一个对象怎么去引用另一个对象呢?
----->在之前是直接new 一个对象出来,后来呢,有人说这些个工作直接给容器处理吧;
---->好的,就有了xml配置,xml之间的配置功能就是在初始化的时候就告诉容器,对象之间的依赖关系;
---->现在可以使用注释的功能,连xml也省了,这样多好啊。
你是不是会觉得我一直都这样做的啊,OK,非常好,只是有一个问题是:除了Spring容器自带的这个功能,我们在项目开发中还有哪里用到了这个设计思想?
概述小结:IOC是Spring容器的内核,AOP、声明式事务等功能都是在此基础上开花结果的。所谓IOC,就是通过容器来控制业务对象之间的依赖关系,而非传统实现中,由代码直接操控。这也就是”控制反转“概念的所在:控制权应用由代码中转到了外部容器,控制权的转移,就是反转。控制权转移带来的好处就是降低了业务对象之间的依赖程度。
二、BeanFactory 和 ApplicationContext
Spring通过一个配置文件描述Bean及Bean之间的依赖关系,
利用Java语言的反射功能实例化Bean并建立Bean之间的依赖关系。Spring 的IOC 容器在完成这些底层工作的基础上,还提供了Bean实例缓存、生命周期管理、Bean实例代理、事件发布、资源装载等高级服务。
Bean工程(
org.springframework.beans.factory.BeanFactory
)是Spring框架最核心的接口,它提供了高级IOC的配置机制。
BeanFactory 使管理不同类型的Java对象成为可能,应用上下文(
org.springframework.context.
ApplicationContext
)建立在BeanFactory基础上,提供了更多面向应用的功能,它提供了国际化支持和框架事件体系,更易于创建实际应用。我们一般称BeanFactory为IOC容器,而称ApplicationContext为应用上下文。但有时为了方便,会将ApplicationContext成为Spring容器。
对于两者的用途,我们可以进行简单划分:
BeanFactory 是Spring的基础设施,不会直接面对开发人员,是面向Spring本身的;ApplicationContext是应用上下文,面向的是Spring框架的开发人员,几乎所有的场合,我们使用的是ApplicationContext而非BeanFactory。
注解:BeanFactory是ApplicationContext的基础,对我们的应用来说基础的基础;ApplicationContext是我们经常会用到的。了解BeanFactory和ApplicationContext的区别对我们理解IOC容器是比较重要的,弄清楚这两种重要容器之间的区别和联系,意味着我们具备了辨别容器系列中不同容器产品的能力。
三、BeanFactory介绍
BeanFactory是一个类工厂,但它和传统的类工厂不同,传统的类工厂仅负责构造一个或几个类的实例,而BeanFactory是类的通用工厂,它可以创建并管理各种类的对象。这些可被创建和管理的对象本身没有什么特别之处,仅是一个POJO,Spring称这些创建和管理的各种对象(Java类)成为Bean。
四、ApplicationContext 介绍
如果说BeanFactory是Spring的心脏,那么ApplicationContext 就是完整的身躯了。ApplicationContext 由BeanFactory派生而来,提供了很多实际应用的功能。就像在建筑材料中的铁是钢的最基础组成部分,但是在只有铁的时候是没有办法用在汽车身上的,练成钢之后,才能让生产不同种类的钢来满足不同的生产需求。
在BeanFactory中,很多功能需要以编程的方式实现,但是在ApplicationContext中可以以配置文件的形式来实现。
在获取ApplicationContext实例后,就可以像BeanFactory一样调用getBean(beanName)返回Bean了。ApplicationContext 的初始化和BeanFactory的初始化有一个重大的不同之处就是:BeanFactory在初始化时并没有一次性获取所有的Bean,而是访问一次Bean,就实例化目标Bean;而ApplicationContext是在初始化的时候就一次性得到所有的Bean,并管理Bean及其依赖关系。因此ApplicationContext的初始化会比BeanFactory时间稍长一些,而这些时间是值得的。
而在Spring3之后,是直接注解的方式来实例化和管理Bean及其依赖关系的, 而完成这个管理的是一个名为JavaConfig的子项目,一个标注@Configuration 注解的POJO 即可提供Spring 所需的Bean配置信息。如下图:
和配置的方式项目相比较,基于注解的方式更加灵活,也更容易的让开发者控制Bean的初始化。
下面是ApplicationContext结构:
从上图可以看到,ApplicationContext只是一个接口,其下的接口是ConfigureableApplicationContext,有两个实现类,一个是ClassPathXmlApplicationContext 和FileSystemXmlApplicationContext 两个。
ApplicationContext,新增两个主要主要方法:refresh()和close(),让ApplicationContext具有启动,刷新和关闭上下文的能力;
ApplicationContext在初始化上下文时就实例化所有单例的Bean。
WebApplicationContext 是专门为WEB应用而准备的,它允许从相对于WEB根目录的路径中完成初始化工作。
五、Bean的装配
要使应用程序中的Spring容器成功启动,需要同时具备以下3方面的条件。
1、Spring框架的类的类包都已经放到应用程序的类路径下。(所需要的jar包得包含进去)
2、应用程序为Spring提供完备的Bean配置信息;(也就是说在配置Bean的时候信息不能缺失,不能错误,否则会报错)
3、Bean的类都已经方法都已经放到应用程序的类的路径下。(这个是必须的,你之所以配置,是以为有这个类,对吧?)
在项目中最容易出的错就是类找不到,但是在项目中又能找到这个类,那就很有可能是这个类的配置有问题,如果是XML配置,可能就是配置不对,如果是注入的方式,那就是忘了写注入Bean的注释。
下图是Spring容器,Bean配置信息,Bean实现类及应用程序这四者的相互关系:
1、Bean的配置,无论是XML文件配置还是注解的方式,在实例化时先独立Bean配置信息;
2、有了这些Bean的配置信息之后,将这些Bean放到Bean定义注册表中注册;
3、将Bean实例化到Spring容器中,也就是图中看到的Bean缓冲池;
4、到此,如果配置信息,没有错误,那么这些个过程将在很短的时间内完成;
5、在应用程序使用Bean时,就会直接从Bean缓存池中得到其实例及其与其他Bean的依赖关系。
参考书目:《Spring3.0就这么简单》
《Spring技术内幕:深入理解Spring架构及设计原理》(过于深入,对开发来说作用不大,但是对于架构来说,其思想很值得借鉴)
《Spring3.X企业应用开发实战》