IOC 全称控制反转,英文名为 Inversion of Control,它还有一个别名为 DI(Dependency Injection),即依赖注入。
在我们刚接触Spring的时候,我们就听说了IOC,但是对于IOC的理解,貌似有些苦难。
我们对他的理解可能都是停留在以下内容:
就是一个类的实例化过程本来应由有我们自己控制new的过程,现在我们可以把控制权交给Spring框架来处理实例化对象。(获得对象的方式反转了)
降低程序间的耦合(依赖关系)
从字面看上去很简单,“控制”AND “反转”。但是我们如何理解“控制反转”呢?
那么我们就应该弄清以下四个问题:
在回答这四个问题之前,我们先看 IOC 的定义:
所谓 IOC ,就是由 Spring IOC 容器来负责对象的生命周期和对象之间的关系
上面这句话是整个 IOC 理论的核心。如何来理解这句话?我们引用一个例子来走阐述(看完该例子上面四个问题也就不是问题了)。
找女朋友,一般情况下我们是如何来找女朋友的呢?首先我们需要根据自己的需求(漂亮、身材好、性格好)找一个妹子(喜欢吃软饭也可能是有钱阿姨啥的),然后到处打听她的兴趣爱好、微信、电话号码,然后各种投其所好送其所要,最后追到手。如下:
/** * 年轻小伙子 */ public class YoungMan { private BeautifulGirl beautifulGirl; YoungMan(){ // 可能你比较牛逼哈,指腹为婚 // beautifulGirl = new BeautifulGirl(); } public void setBeautifulGirl(BeautifulGirl beautifulGirl) { this.beautifulGirl = beautifulGirl; } public static void main(String[] args){ YoungMan you = new YoungMan(); BeautifulGirl beautifulGirl = new BeautifulGirl("你的各种条件"); beautifulGirl.setxxx("各种投其所好"); // 然后你有女票了,真厉害 you.setBeautifulGirl(beautifulGirl); } }
这就是我们通常做事的方式,如果我们需要某个对象,一般都是采用这种直接创建的方式(new BeautifulGirl()),这个过程复杂而又繁琐,而且我们必须要面对每个环节,而且使用完成之后我们还要复杂销毁它,这种情况下我们的对象与它所依赖的对象耦合在一起。
其实我们需要思考一个问题?我们每次用到自己依赖的对象真的需要自己去创建吗?我们知道,我们依赖对象其实并不是依赖该对象本身,而是依赖它所提供的服务,只要在我们需要它的时候,它能够及时提供服务即可,至于它是我们主动去创建的还是别人送给我们的,其实并不是那么重要。再说了,相比于自己千辛万苦去创建它还要管理善后而言,直接有人送过来是不是显得更加好呢?
这个给我们送东西的“人” 就是 IOC ,在上面的例子中,它就相当于一个婚介公司,作为一个婚介公司它管理着很多男男女女的资料,当我们需要一个女朋友的时候,直接跟婚介公司提出我们的需求,婚介公司则会根据我们的需求提供一个妹子给我们,我们只需要负责谈恋爱,生猴子就行了。你看,这样是不是很简单明了。
诚然,作为婚介公司的 IOC 帮我们省略了找女朋友的繁杂过程,将原来的主动寻找变成了现在的被动接受,更加简洁轻便。你想啊,原来你还得鞍马前后,各种巴结,什么东西都需要自己去亲力亲为,现在好了,直接有人把现成的送过来,多么美妙的事情啊。所以,简单点说,IOC 的理念就是让别人为你服务,如下图(摘自Spring揭秘):
在没有引入 IoC 的时候,被注入的对象直接依赖于被依赖的对象,有了 IOC 后,两者及其他们的关系都是通过 Ioc Service Provider 来统一管理维护的。被注入的对象需要什么,直接跟 IoC Service Provider 打声招呼,后者就会把相应的被依赖对象注入到被注入的对象中,从而达到 IOC Service Provider 为被注入对象服务的目的。所以 IOC 就是这么简单!原来是需要什么东西自己去拿,现在是需要什么东西让别人(IOC Service Provider)送过来
现在在看上面那四个问题,答案就显得非常明显了:
妹子有了,但是如何拥有妹子呢?这也是一门学问。
所以,IOC Service Provider 为被注入对象提供被依赖对象也有如下几种方式:构造方法注入、stter方法注入、接口注入。
① 构造器注入
构造器注入,顾名思义就是被注入的对象通过在其构造方法中声明依赖对象的参数列表,让外部知道它需要哪些依赖对象。
YoungMan(BeautifulGirl beautifulGirl) { this.beautifulGirl = beautifulGirl; }
构造器注入方式比较直观,对象构造完毕后就可以直接使用,这就好比你出生你家里就给你指定了你媳妇。
② setter 方法注入
对于 JavaBean 对象而言,我们一般都是通过 getter 和 setter 方法来访问和设置对象的属性。所以,当前对象只需要为其所依赖的对象提供相对应的 setter 方法,就可以通过该方法将相应的依赖对象设置到被注入对象中。如下:
public class YoungMan { private BeautifulGirl beautifulGirl; public void setBeautifulGirl(BeautifulGirl beautifulGirl) { this.beautifulGirl = beautifulGirl; } }
相比于构造器注入,setter 方式注入会显得比较宽松灵活些,它可以在任何时候进行注入(当然是在使用依赖对象之前),这就好比你可以先把自己想要的妹子想好了,然后再跟婚介公司打招呼,你可以要林志玲款式的,赵丽颖款式的,甚至凤姐哪款的,随意性较强。
③ 接口方式注入
接口方式注入显得比较霸道,因为它需要被依赖的对象实现不必要的接口,带有侵入性。一般都不推荐这种方式。
关于 IOC 理论部分,在这里就不在阐述,这里推荐几篇博客阅读:
先看下图(摘自:http://singleant.iteye.com/blog/1177358)
该图为 ClassPathXmlApplicationContext 的类继承体系结构,虽然只有一部分,但是它基本上包含了 IOC 体系中大部分的核心类和接口。
下面我们就针对这个图进行简单的拆分和补充说明
org.springframework.core.io.Resource,对资源的抽象。它的每一个实现类都代表了一种资源的访问策略,如 ClassPathResource、RLResource、FileSystemResource 等。
有了资源,就应该有资源加载,Spring 利用 org.springframework.core.io.ResourceLoader 来进行统一资源加载,类图如下:
org.springframework.beans.factory.BeanFactory,是一个非常纯粹的 bean 容器,它是 IOC 必备的数据结构,其中 BeanDefinition 是它的基本结构。BeanFactory 内部维护着一个BeanDefinition map ,并可根据 BeanDefinition 的描述进行 bean 的创建和管理。
org.springframework.beans.factory.config.BeanDefinition ,用来描述 Spring 中的 Bean 对象。
BeanDefinition 类图
org.springframework.beans.factory.support.BeanDefinitionReader 的作用是读取 Spring 的配置文件的内容,并将其转换成 IOC 容器内部的数据结构 :BeanDefinition 。
org.springframework.context.ApplicationContext ,这个就是大名鼎鼎的 Spring 容器,它叫做应用上下文,与我们应用息息相关。它继承 BeanFactory ,所以它是 BeanFactory 的扩展升级版,如果BeanFactory 是屌丝的话,那么 ApplicationContext 则是名副其实的高富帅。由于 ApplicationContext 的结构就决定了它与 BeanFactory 的不同,其主要区别有:
我们在最开始学习 Spring 的时候,就接触 IOC 了,IOC是Spring相对比较核心的概念,本文上面五个体系可以说是 Spring IOC 中最核心的部分,只有深入理解了IOC,在我们阅读Spring源码的时候,我们才能够更更加清晰。
此文章为作者学习Spring机制记录的笔记,其中会涉及到别人的文章内容以及书籍的内容,如有雷同纯属借鉴。同时由于知识面的能力问题,文章难免会有错误之处,在不断的改正,如有错误之处,还请各位大佬指正哦。