百度百科表述:

控制反转(Inversion of Control,缩写为IOC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup,简称DL)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。

上面的描述看起来是不是根本不知道所以然,我们接下来用通俗易懂的语言来和大家进行讲解下。

 

一、IOC了解

(1)IOC(控制反转)

控制反转(Inversion of Control,缩写为IOC)(不是什么技术,而是一种设计思想):把对象的创建和对象之间的调用过程交给容器进行管理。

       IOC意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。如何理解好IOC呢?理解好IOC的关键是要明确“谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些方面反转了”,那我们来深入分析一下:

(1)谁控制了谁,控制什么:传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IOC是有专门一个容器来创建这些对象,即由IOC容器来控制对象的创建;

谁控制谁?当然是IOC容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)。

(2)为何是反转,哪些方面反转了:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。

 

(2)使用IOC目的

为了降低耦合度。

 

(3)IOC的实现方式

两种实现: 依赖查找(DL:Dependency Lookup)和依赖注入(DI:Dependency Injection)。

(1)DI(依赖注入):使用setter和Constructor将对象进行注入。

(2)DL(依赖查找):用户自己去是使用 API 进行查找资源和组装对象,有侵入性。比如:使用JNDI查找是对象。

MyService myService = (MyService)(jndiContext.lookup("NameOfMyService"));

二、代码示例理解IOC

在接下来我们使用代码的方式来理解下IOC控制反转的这个设计思想。

我们有两个服务,用户服务和打印机服务,用户服务会调用打印机的服务。

2.1 传统的编码方式

定义打印机的服务PrinterService:

package com.kfit.test.test1;

public class PrinterService {

    public void print(){
        System.out.println("打印机进行打印...");
    }

}

定义用户服务UserService,这里使用new的方式进行注入PrinterService:

package com.kfit.test.test1;

public class UserService {

    private  PrinterService printerService = new PrinterService();


    public void usePrinter(){
        //用户相关的操作
        System.out.println("进行用户相关的操作...");

        //调用打印机进行打印数据
        printerService.print();
    }

}

测试类Test:

package com.kfit.test.test1;

public class Test {

    public static void main(String[] args) {
        UserService userService = new UserService();
        userService.usePrinter();
    }
}

在这个例子当中,我们的程序控制了这个对象的注入,如果我们的实现PrinterService变了,那么就需要进行修改UserService代码,这就是所谓的强耦合。

 

2.2 Spring的IOC方式

定义打印机的服务PrinterService:

package com.kfit.test.test1;

public class PrinterService {

    public void print(){
        System.out.println("打印机进行打印...");
    }

}

定义用户服务UserService,这里并没有使用new的方式进行注入PrinterService,在这里我们只是声明了有这么一个属性而已:

package com.kfit.test.test2;

public class UserService {

    //这里并未使用new的方式进行注入对象.
    private PrinterService printerService;


    public void usePrinter(){
        //用户相关的操作
        System.out.println("进行用户相关的操作...");

        //调用打印机进行打印数据
        printerService.print();
    }

}

定义Spring的配置文件applicationContext-test2.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="printerService" class="com.kfit.test.test2.PrinterService"></bean>

    <bean id="userService" class="com.kfit.test.test2.UserService">
        <property name="printerService" ref="printerService" />
    </bean>

</beans>

这里就是使用配置文件的方式进行对象的创建和对象的注入。

最后就可以进行测试了:

package com.kfit.test.test2;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {

    public static void main(String[] args) {
        //加载配置文件
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext-test2.xml");
        //获取Spring容器中的对象.
        UserService userService = ctx.getBean(UserService.class);
        userService.usePrinter();
    }
}

在这个例子当中,Spring IOC容器控制了这个对象的注入,如果我们的实现PrinterService变了,那么就需要进行修改XML配置即可,这就是所谓的低耦合。

 

 

三、生活例子来理解控制反转

3.1 生病

你感冒了需要打针,以前条件差没医生的时候,总是要在自己去找草药来熬药喝。(是不是像代码中直接new对象的方式)。

现在有了医生后我们就只需要告诉医生,我感冒了需要打一针,至于这个针管里的药怎么做的,什么时候做的(现实还是要看一下,哈哈),我不需要知道。我只要打了能用,会好就可以了,在打针的时侯,医生就会拿出一个注射器,注射到我的体内,这样就完成了。而我需要依赖注射药物才能好,而这个药物是由医生注入到我体内的,依赖注入的名字就这么来的。

3.2 租房子

以前我们找房子需要自己去找,然后联系房东;现在呢,有了中介了之后,我们直接把房子的要求告诉中介,中介会把符合条件的房子提供给我们。

3.3 皇帝宠幸佳丽

套用好莱坞的一句名言就是:你呆着别动,到时我会找你。

什么意思呢?就好比一个皇帝和太监

有一天皇帝想幸某个美女,于是跟太监说,今夜我要宠幸美女。

皇帝往往不会告诉太监,今晚几点会回宫,会回哪张龙床,他只会告诉太监他要哪位美女

其它一切都交由太监去安排,到了晚上皇帝回宫时,自然会有美女出现在皇帝的龙床上。

这就是控制反转(IoC Inversion of Control),而把美女送到皇帝的寝宫里面去就是注射(DI dependence Inversion)

太监就是是框架里面的注射控制器类BeanFactory,负责找到美女并送到龙床上去

整个后宫可以看成是Spring框架,美女就是Spring控制下的JavaBean

而传统的模式就是一个饥渴男去找小姐出台,找领班,帮助给介绍一个云云,于是领班就开始给他张罗。

介绍一个合适的给他,完事后,再把小姐还给领班,下次再来

这个过程中,领班就是查询上下文Context,领班的一个职能就是给客户找到他们所要的小姐。

这就是lookup()方法,领班手中的小姐名录就是JNDI//Java Naming and Directory Interface

小姐就是EJB,饥渴男是客户端,青楼是EJB容器

看到区别了么?饥渴男去找小姐出台很麻烦,不仅得找,用完后还得把小姐给还回去

而皇帝爽翻了,什么都不用管,交给太监去处理,控制权转移到太监手中去了

而不是皇帝,必要时候由太监给注射进去就可以了

看到Spring的美妙了吧,Spring还提供了与多个主流框架的支持,可以和其它开源框架集成。

看到区别了么?饥渴男去找小姐出台很麻烦,不仅得找,用完后还得把小姐给还回去

而皇帝爽翻了,什么都不用管,交给太监去处理,控制权转移到太监手中去了

而不是皇帝,必要时候由太监给注射进去就可以了

看到Spring的美妙了吧,Spring还提供了与多个主流框架的支持,可以和其它开源框架集成