在我们学习spring和面试的过程中,有一个核心的内容就叫做IOC。IOC(Inversion of Control),中文翻译即“控制反转”,它不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将设计好的对象交给容器控制,而不是传统的在对象内部直接控制。
所以通过以上的介绍,我们或许有这样的疑问:谁控制谁,控制什么,怎么反转,哪些方面反转了?
带着这些疑问,我们来一起学习下Ioc的知识!
1. IOC理论的诞生
前面说到Ioc并不是一种技术,而是一种理论一种设计思想,就跟面向对象一样。在采用面向对象设计思想开发的系统中,它底层的实现是由N个对象组成的,所有的对象通过彼此的合作,最终实现系统的业务逻辑。就像一辆汽车,它的运行其实是有很多个独立的器械之间的相互合作来推动的。比如发动机、底盘、离合器、变数器、机箱等等,这些主要的零件又是由各类各样的螺丝、转轮等组成。
就像组成汽车的各个零件对象相互配合,少了谁也不行一样。系统的对象之间的耦合关系即是无法避免的,也是十分必要的,因为这是协同工作的基础。但是随着一些大型系统的规模越来越庞大,对象之间的依赖关系也变得越来越复杂,这就导致出现对象之间的多重依赖性关系。对象之间耦合关系过于复杂的系统,它的设计、迭代也会变得复杂,因为这些对象和系统之前的关系会出现牵一发而动全身的情形。
为了解决对象之间的耦合度过高的问题,软件专家Michael Mattson提出了IOC理论,用来实现对象之间的“解耦”,这个理论已经被成功地应用到实践当中,比如现在很多的项目都采用了IOC设计思想的Spring框架。
2. 什么是IOC
IOC是Inversion of Control的缩写,翻译成中文即“控制反转”。
对于Spring框架来说,就是由Spring来负责控制对象的生命周期和对象间的关系。举个例子,如果我们想吃宫保鸡丁,然后自己做,这时候我们就需要去买食材、然后做饭、吃完饭之后还得处理垃圾、洗碗,这个过程其实是很复杂的,因为我们必须自己设计和面对整个过程。但是如果采用了Ioc的设计思想,我们这时候只需要做一件事事情——去饭店点菜。不论我们想吃宫保鸡丁、水煮肉片、烤鱼还是什么任何其他口味、菜系或者类型的食物,饭店都会按照我们的需求提供给我们,吃完之后我们也不需要处理垃圾(销毁对象)。这个整个的过程都不需要我们自己参与控制,而是通过饭店这样一个类似于容器的来进行控制。
Spring的Ioc设计思想就是这样,所有的类都会在Spring容器中登记,告诉Spring你是个什么东西,需要什么东西,然后Spring就会在系统运行到恰当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西,当你不需要的时候就在销毁。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。也就是借助于“第三方”工具(IOC容器)来实现具有依赖关系的对象之间的解耦,类似于下图:
通过使用中间的IOC容器,使得A、B、C、D这4个对象没有了直接的耦合关系,它们之间的传动全部依靠“第三方”的Ioc容器。
在没有引入IOC容器,对象A依赖于对象B,那么对象A在初始化或者运行到某一点的时候,自己必须主动去创建对象B或者使用已经创建的对象B。无论是创建还是使用对象B,控制权都在自己手上。
引入IOC容器之后,对象A与对象B之间失去了直接联系,所以,当对象A运行到需要对象B的时候,IOC容器会主动创建一个对象B注入到对象A需要的地方。
通过前后的对比,我们不难看出来:对象A获得依赖对象B的过程,由主动行为变为了被动行为,控制权颠倒过来了,这就是“控制反转”这个名称的由来。
全部对象的控制权全部由“第三方”IOC容器进行控制,所以,IOC容器成了整个系统的关键核心,它起到了一种类似“粘合剂”的作用,把系统中的所有对象粘合在一起发挥作用,如果没有IOC容器,对象与对象之间会彼此失去联系。
3. 什么IOC容器
前面说到了控制反转的核心是Ioc容器,那么什么是Ioc容器呢?
Ioc容器就是具有依赖注入功能的容器,Ioc容器负责实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。应用程序无需直接在代码中new相关的对象,应用程序由IOC容器进行组装。在Spring IOC容器的代表就是org.springframework.beans包中的BeanFactory接口,BeanFactory接口提供了IOC容器最基本功能;而org.springframework.context包下的ApplicationContext接口扩展了BeanFactory,还提供了与Spring AOP集成、国际化处理、事件传播及提供不同层次的context实现 (如针对web应用的WebApplicationContext)。简单说, BeanFactory提供了IOC容器最基本功能,而 ApplicationContext 则增加了更多支持企业级功能支持。ApplicationContext完全继承BeanFactory,因而BeanFactory所具有的语义也适用于ApplicationContext。
由Ioc容器管理的那些对象一般称之为它Bean, Bean就是由Spring容器初始化、装配及管理的对象,除此之外,bean就与应用程序中的其他对象没有什么区别。
所以Ioc容器实际上就是一个map,里面存的是各种对象,在项目启动的时候会读取配置文件中的bean节点,根据全限定类名使用反射创建对象放到map里,扫描到打上@repository、@service、@controller、@component这些注解的类然后通过使用反射创建对象放到map中。这时候map中就存在各种对象了,后面代码在需要使用到里面的对象的时候,再通过依赖注入(DI)。
4. 依赖注入(DI)
前面说到项目启动的一开始通过读取配置文件将对象全部存放到ioc容器中,然后再在合适的时机进行依赖注入。我们知道控制反转就是将获得依赖对象的控制权交给了ioc容器。也就是说反转的具体就是获得依赖对象的过程。
控制被反转之后,获得依赖对象的过程就由本来的自动生成变为了交给IOC容器来主动注入。所以控制反转的另一个名字叫做依赖注入(DI,Dependency Injection)。所谓依赖注入,就是由IOC容器在运行期间,动态地将某种依赖关系注入到对象之中,依赖注入也就是实现Ioc的方法。
所以,依赖注入(DI)和控制反转(IOC)是从不同的角度的描述的同一件事情,就是指通过引入IOC容器,利用依赖关系注入的方式,实现对象之间的解耦。
比如对象A依赖于对象B,当对象A需要用到对象B的时候,IOC容器就会立即创建一个对象B送给对象A。IOC容器就是一个对象制造工厂(BeanFactory),需要什么,就会给你送去什么,然后直接使用就可以了,而再也不用去关心你所用的东西是如何制成的,也不用关心最后是怎么被销毁的,这一切全部由IOC容器包办,这也是工厂方法设计模式的思想了。Ioc就是典型的工厂模式,通过sessionfactory去注入实例。
5. 举一个小栗子
前面介绍了这么多,接下来看一个简单的例子。
创建一个spring项目,工程结构如下图所示:
首先创建一个helloservice接口,然后创建一个其实现类,代码分别如下:
public interface HelloService { public void say(); } 复制代码
public class HelloServiceImp implements HelloService { public void say(){ System.out.println("Hello World"); } } 复制代码
接口和其实现类以及方法都开发好了,那如何使用Spring IOC容器来管理它们呢?这就需要配置文件,让IOC容器知道要管理哪些对象。创建一个配置文件helloworld.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <!-- id 表示组件的名字,class表示组件类 --> <bean id="helloService" class="com.jiang.HelloServiceImp" /> </beans> 复制代码
最后我们就需要获取IOC容器并实现功能。
首先应该实例化一个IOC容器,然后从容器中获取需要的对象,然后调用接口完成我们需要的功能,创建一个测试代码代码示例如下:
package com.jiang; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestHello { @Test public void test(){ // 1、读取配置文件实例化一个IOC容器 ApplicationContext context = new ClassPathXmlApplicationContext("helloword.xml"); // 2、从容器中获取Bean,注意此处完全“面向接口编程,而不是面向实现” HelloService helloService = context.getBean("helloService",HelloService.class); // 3、执行业务逻辑 helloService.say(); } } 复制代码
输出结果如下:
6、总结
以上就是我对于ioc的理解。ioc的思想最核心的地方在于,资源不由使用资源的双方管理,而由不使用资源的第三方管理,这可以带来很多好处。第一,资源集中管理,实现资源的可配置和易管理。第二,降低了使用资源双方的依赖程度,也就是耦合度。这样就提高系统的可维护性,而且非常便于进行单元测试。并且各个组件之间的依赖性降低了,就提高了系统的可重复性和可拓展性
ioc中有两个核心的可以理解为技术点的就是工厂设计模式以及反射。IOC容器的工作模式看做是工厂模式的升华,可以把IOC容器看作是一个工厂,这个工厂里要生产的对象都在配置文件中给出定义,然后利用反射,根据配置文件中给出的类名生成相应的对象。
如果你觉得本文不错,就点赞分享给更多的人吧!
如果你觉得文章有不足之处,或者更多的想法和理解,欢迎指出讨论!