想要了解控制反转( Inversion of Control ), 我觉得有必要先了解软件设计的一个重要思想:依赖倒置原则(Dependency Inversion Principle )。

什么是依赖倒置原则?

假设我们设计一辆汽车:先设计轮子,然后根据轮子大小设计底盘,接着根据底盘设计车身,最后根据车身设计好整个汽车。这里就出现了一个“依赖”关系:汽车依赖车身,车身依赖底盘,底盘依赖轮子。

这样的设计看起来没问题,但是可维护性却很低。假设设计完工之后,上司却突然说根据市场需求的变动,要我们把车子的轮子设计都改大一码。这下我们就蛋疼了:因为我们是根据轮子的尺寸设计的底盘,轮子的尺寸一改,底盘的设计就得修改;同样因为我们是根据底盘设计的车身,那么车身也得改,同理汽车设计也得改——整个设计几乎都得改!
 
 
现在换一种思路。我们先设计汽车的大概样子,然后根据汽车的样子来设计车身,根据车身来设计底盘,最后根据底盘来设计轮子。这时候,依赖关系就倒置过来了:轮子依赖底盘, 底盘依赖车身, 车身依赖汽车。

 
再说要改动轮子的设计,我们就只需要改动轮子的设计,而不需要动底盘,车身,汽车的设计了。
 
 
这就是依赖倒置原则——把原本的高层建筑依赖底层建筑“倒置”过来,变成底层建筑依赖高层建筑。高层建筑决定需要什么,底层去实现这样的需求,但是高层并不用管底层是怎么实现的。这样就不会出现前面的“牵一发动全身”的情况。
 

 
 
控制反转(Inversion of Control) 就是依赖倒置原则的一种代码设计的思路。具体采用的方法就是所谓的依赖注入(Dependency Injection)。
这几种概念的关系大概如下:

为了理解这几个概念,我们还是用上面汽车的例子。只不过这次换成代码。
 
我们先定义四个Class,车,车身,底盘,轮胎。然后初始化这辆车,最后跑这辆车。代码结构如下:

 
这就相当于上面第一个例子,上层建筑依赖下层建筑——每一个类的构造函数都直接调用了底层代码的构造函数。假设我们需要改动一下轮胎(Tire)类,把它的尺寸变成动态的,而不是一直都是30。我们需要这样改:

由于我们修改了轮胎的定义,为了让整个程序正常运行,我们需要做以下改动:

由此我们可以看到,仅仅是为了修改轮胎的构造函数,这种设计却需要修改整个上层所有类的构造函数!在软件工程中,这样的设计几乎是不可维护的——在实际工程项目中,有的类可能会是几千个类的底层,如果每次修改这个类,我们都要修改所有以它作为依赖的类,那软件的维护成本就太高了。
 
所以我们需要进行控制反转(IoC),及上层控制下层,而不是下层控制着上层。我们用依赖注入(Dependency Injection)这种方式来实现控制反转。所谓依赖注入,就是把底层类作为参数传入上层类,实现上层类对下层类的“控制”。这里我们用构造方法传递的依赖注入方式重新写车类的定义:

 
这里我们再把轮胎尺寸变成动态的,同样为了让整个系统顺利运行,我们需要做如下修改:

 

这里我只需要修改轮胎类就行了,不用修改其他任何上层类。这显然是更容易维护的代码。不仅如此,在实际的工程中,这种设计模式还有利于不同组的协同合作和单元测试:比如开发这四个类的分别是四个不同的组,那么只要定义好了接口,四个不同的组可以同时进行开发而不相互受限制;而对于单元测试,如果我们要写Car类的单元测试,就只需要Mock一下Framework类传入Car就行了,而不用把Framework, Bottom, Tire全部new一遍再来构造Car。

这里我们是采用的构造函数传入的方式进行的依赖注入。其实还有另外两种方法:Setter传递和接口传递。这里就不多讲了,核心思路都是一样的,都是为了实现控制反转。

那什么是控制反转容器(IoC Container)呢?其实上面的例子中,对车类进行初始化的那段代码发生的地方,就是控制反转容器。

因为采用了依赖注入,在初始化的过程中就不可避免的会写大量的new。这里IoC容器就解决了这个问题。这个容器可以自动对你的代码进行初始化,你只需要维护一个Configuration(可以是xml可以是一段代码),而不用每次初始化一辆车都要亲手去写那一大段初始化的代码。这是引入IoC Container的第一个好处。

IoC Container的第二个好处是:我们在创建实例的时候不需要了解其中的细节。在上面的例子中,我们自己手动创建一个车instance时候,是从底层往上层new的:

这个过程中,我们需要了解整个Car/Framework/Bottom/Tire类构造函数是怎么定义的,才能一步一步new/注入。

而IoC Container在进行这个工作的时候是反过来的,它先从最上层开始往下找依赖关系,到达最底层之后再往上一步一步new(有点像深度优先遍历):

这里IoC Container可以直接隐藏具体的创建实例的细节,在我们来看它就像一个工厂:

我们就像是工厂的客户。我们只需要向工厂请求一个Car实例,然后它就给我们按照Config创建了一个Car实例。我们完全不用管这个Car实例是怎么一步一步被创建出来。

实际项目中,有的Service Class可能是十年前写的,有几百个类作为它的底层。假设我们新写的一个API需要实例化这个Service,我们总不可能回头去搞清楚这几百个类的构造函数吧?IoC Container的这个特性就很完美的解决了这类问题——因为这个架构要求你在写class的时候需要写相应的Config文件,所以你要初始化很久以前的Service类的时候,前人都已经写好了Config文件,你直接在需要用的地方注入这个Service就可以了。这大大增加了项目的可维护性且降低了开发难度。

 
 

Spring:Spring-IOC简介的更多相关文章

  1. [spring源码学习]一、IOC简介

    一.程序实例 假设一个简单地实例,我们有一个人,人可能有姓名,年龄等属性,每天上下班的时候需要坐车,他可能做小轿车,suv等,这样一个场景.我们很容易想到如下代码: 1.人的对象类,包括两个属性,姓名 ...

  2. Spring.Net 技术简介 IOC and DI

    一 简单介绍            IOC 控制转移,就是将创建放到容器里,从而达到接耦合的目的,DI是 在容器创建对象的时候,DI读取配置文件,然后给对象赋默认值,两者一般结合使用,实现注入.   ...

  3. 一、 Spring IOC 简介

    一 . 什么是IOC(Inversion of Control) IOC :控制反转,也称为依赖注入(DI).听起来有点云里雾里,他到底是个啥,为什么Spirng中最基础的就是它? 我们的项目其实都是 ...

  4. Spring框架IOC容器和AOP解析

    主要分析点: 一.Spring开源框架的简介  二.Spring下IOC容器和DI(依赖注入Dependency injection) 三.Spring下面向切面编程(AOP)和事务管理配置  一.S ...

  5. Spring Web Flow 简介

    Spring Web Flow 简介 博客分类: 转载 SSH 最近在TSS上看到了一片介绍Spring Web Flow的文章,顺便就翻译了下来,SWF的正式版估计要到6月份才能看到了,目前的例子都 ...

  6. Spring 实践 -IoC

    Spring 实践 标签: Java与设计模式 Spring简介 Spring是分层的JavaSE/EE Full-Stack轻量级开源框架.以IoC(Inverse of Control 控制反转) ...

  7. Spring中AOP简介与切面编程的使用

    Spring中AOP简介与使用 什么是AOP? Aspect Oriented Programming(AOP),多译作 "面向切面编程",也就是说,对一段程序,从侧面插入,进行操 ...

  8. (二)Spring 之IOC 详解

    第一节:spring ioc 简介 IOC(控制反转:Inversion of Control),又称作依赖注入dependency injection( DI ),是一种重要的面向对象编程的法则来削 ...

  9. Spring框架IOC容器和AOP解析 非常 有用

    Spring框架IOC容器和AOP解析   主要分析点: 一.Spring开源框架的简介  二.Spring下IOC容器和DI(依赖注入Dependency injection) 三.Spring下面 ...

  10. Spring && 实验IOC

    一.Spring作用 1.生态体系庞大,全能型选手![springmvc是其一个子模块,jdbcTemplate能直接操作数据库!]    2.将其他组件粘合在一起    3.IOC容器和AOP[As ...

随机推荐

  1. 7.6 passwd:修改用户密码

    7.6 passwd:修改用户密码     passwd命令可以修改用户密码及密码过期时间等内容,是工作中很常用的命令.普通用户和超级用户都可以运行passwd命令,但普通用户只能更改自身的用户密码, ...

  2. STM32电路设计注意

    以后画STM32的电路板 VDDA一定要接 张JF说  VDDA是给内部的时钟电路供电的 还有一定要留 串口或者  下载调试口(串口) 或者显示器接口 输入输出设备最好都留着 这样才能方便调试

  3. 简单读读源码 - dubbo多提供者(provider)配置方法

    简单读读源码 - dubbo多提供者(provider)配置方法 消费者端dubbo的yml配置 dubbo: consumer: timeout: 300000 protocol: name: du ...

  4. 解决latex数学公式渲染不正确及行内公式中文渲染乱码问题

    问题 之前数学OCR渲染数学公式用的 katex 来渲染,前端解决方案,我们的进行公式编写的时候是需要输入中文的,如: Fe_{2}O_{3} + 3 C O \stackrel{高温}{=} 2 F ...

  5. VMware vCenter Server 7.0 U2b/6.7 U3n/6.5 U3p,修复 vSphere Client 高危安全漏洞

    vSphere Client(HTML5)中的多个漏洞已秘密报告给 VMware.这里提供了更新和解决方法来解决受影响的 VMware 产品中的这些漏洞. 详见:VMSA-2021-0010 威胁描述 ...

  6. Sparse R-CNN: End-to-End Object Detection with Learnable Proposals 论文解读

    前言 事实上,Sparse R-CNN 很多地方是借鉴了去年 Facebook 发布的 DETR,当时应该也算是惊艳众人.其有两点: 无需 nms 进行端到端的目标检测 将 NLP 中的 Transf ...

  7. 27.Qt Quick QML-State、Transition

    1.State所有组件均具有默认状态,该状态定义对象和属性值的默认配置.可以通过向States属性添加State组件来定义新状态,以允许组件在不同配置之间切换.许多用户界面设计都是由State状态实现 ...

  8. 让ThreadPoolExecutor的workQueue占满时自动阻塞submit()方法

    public class BlockingSubmitExecutor { private ExecutorService executor = new ThreadPoolExecutor(2, 2 ...

  9. 在gin框架中使用JWT

    在gin框架中使用JWT JWT全称JSON Web Token是一种跨域认证解决方案,属于一个开放的标准,它规定了一种Token实现方式,目前多用于前后端分离项目和OAuth2.0业务场景下. 什么 ...

  10. ASIC设计-终极指南

    ASIC设计-终极指南 ASIC Design – The Ultimate Guide ASIC设计-终极指南 ASICs代表特定于应用的集成电路,指的是针对特定应用而设计的半导体解决方案,与其他解 ...