`

透透彻彻IoC(你没有理由不懂!)

阅读更多
引述:IoC(控制反转:Inverse of Control)是Spring容器的内核,AOP、声明式事务等功能在此基础上开花结果。但是IoC这个重要的概念却比较晦涩隐讳,不容易让人望文生义,这不能不说是一大遗憾。不过IoC确实包括很多内涵,它涉及代码解耦、设计模式、代码优化等问题的考量,我们打算通过一个小例子来说明这个概念。

通过实例理解IoC的概念

    贺岁大片在中国已经形成了一个传统,每到年底总有多部贺岁大片纷至沓来让人应接不暇。在所有贺岁大片中,张之亮的《墨攻》算是比较出彩的一部。该片讲述了战国时期墨家人革离帮助梁国反抗赵国侵略的个人英雄主义故事,恢宏壮阔、浑雄凝重的历史场面相当震撼。其中有一个场景:当刘德华所饰演的墨者革离到达梁国都城下,城上梁国守军问到:“来者何人?”刘德华回答:“墨者革离!”我们不妨通过一个Java类为这个“城门叩问”的场景进行编剧,并借此理解IoC的概念:
代码清单3-1  MoAttack:通过演员安排剧本
public class MoAttack {
   public void cityGateAsk(){
        //①演员直接侵入剧本
	   LiuDeHua ldh = new LiuDeHua();
	   ldh.responseAsk("墨者革离!");
   }
}

   我们会发现以上剧本在①处,作为具体角色饰演者的刘德华直接侵入到剧本中,使剧本和演员直接耦合在一起(图3-1)。

   一个明智的编剧在剧情创作时应围绕故事的角色进行,而不应考虑角色的具体饰演者,这样才可能在剧本投拍时自由地遴选任何适合的演员,而非绑定在刘德华一人身上。通过以上的分析,我们知道需要为该剧本主人公革离定义一个接口:
代码清单3-2  MoAttack:引入剧本角色
public class MoAttack {
   public void cityGateAsk()
   {
        //①引入革离角色接口
	   GeLi geli = new LiuDeHua(); 
       
        //②通过接口开展剧情
	   geli.responseAsk("墨者革离!");  
   }
}

   在①处引入了剧本的角色——革离,剧本的情节通过角色展开,在拍摄时角色由演员饰演,如②处所示。因此墨攻、革离、刘德华三者的类图关系如图 3 2所示:

   可是,从图3 2中,我们可以看出MoAttack同时依赖于GeLi接口和LiuDeHua类,并没有达到我们所期望的剧本仅依赖于角色的目的。但是角色最终必须通过具体的演员才能完成拍摄,如何让LiuDeHua和剧本无关而又能完成GeLi的具体动作呢?当然是在影片投拍时,导演将LiuDeHua安排在GeLi的角色上,导演将剧本、角色、饰演者装配起来(图3-3)。

通过引入导演,使剧本和具体饰演者解耦了。对应到软件中,导演像是一个装配器,安排演员表演具体的角色。
   现在我们可以反过来讲解IoC的概念了。IoC(Inverse of Control)的字面意思是控制反转,它包括两个内容:
  • 其一是控制
  • 其二是反转

  那到底是什么东西的“控制”被“反转”了呢?对应到前面的例子,“控制”是指选择GeLi角色扮演者的控制权;“反转”是指这种控制权从《墨攻》剧本中移除,转交到导演的手中。对于软件来说,即是某一接口具体实现类的选择控制权从调用类中移除,转交给第三方决定。
   因为IoC确实不够开门见山,因此业界曾进行了广泛的讨论,最终软件界的泰斗级人物Martin Fowler提出了DI(依赖注入:Dependency Injection)的概念用以代替IoC,即让调用类对某一接口实现类的依赖关系由第三方(容器或协作类)注入,以移除调用类对某一接口实现类的依赖。“依赖注入”这个名词显然比“控制反转”直接明了、易于理解。

IoC的类型

  从注入方法上看,主要可以划分为三种类型:构造函数注入、属性注入和接口注入。Spring支持构造函数注入和属性注入。下面我们继续使用以上的例子说明这三种注入方法的区别。

构造函数注入

在构造函数注入中,我们通过调用类的构造函数,将接口实现类通过构造函数变量传入,如代码清单3-3所示:
代码清单3-3  MoAttack:通过构造函数注入革离扮演者
public class MoAttack {
   private GeLi geli;
   //①注入革离的具体扮演者
   public MoAttack(GeLi geli){ 
	   this.geli = geli;
   }
	public void cityGateAsk(){
	   geli.responseAsk("墨者革离!");
   }
}

    MoAttack的构造函数不关心具体是谁扮演革离这个角色,只要在①处传入的扮演者按剧本要求完成相应的表演即可。角色的具体扮演者由导演来安排,如代码清单3-4所示:
代码清单3-4  Director:通过构造函数注入革离扮演者
public class Director {
   public void direct(){
        //①指定角色的扮演者
	   GeLi geli = new LiuDeHua();  

        //②注入具体扮演者到剧本中
	   MoAttack moAttack = new MoAttack(geli); 
	   moAttack.cityGateAsk();
   }
}

   在①处,导演安排刘德华饰演革离的角色,并在②处,将刘德华“注入”到墨攻的剧本中,然后开始“城门叩问”剧情的演出工作。

属性注入

   有时,导演会发现,虽然革离是影片《墨攻》的第一主角,但并非每个场景都需要革离的出现,在这种情况下通过构造函数注入相当于每时每刻都在革离的饰演者在场,可见并不妥当,这时可以考虑使用属性注入。属性注入可以有选择地通过Setter方法完成调用类所需依赖的注入,更加灵活方便:
代码清单3-5  MoAttack:通过Setter方法注入革离扮演者
public class MoAttack {
	private GeLi geli;
     //①属性注入方法
	public void setGeli(GeLi geli) {  
		this.geli = geli;
	}
	public void cityGateAsk() {
		geli.responseAsk("墨者革离");
	}
}

   MoAttack在①处为geli属性提供一个Setter方法,以便让导演在需要时注入geli的具体扮演者。
代码清单3-6  Director:通过Setter方法注入革离扮演者
public class Director {
   public void direct(){
	   GeLi geli = new LiuDeHua();
	   MoAttack moAttack = new MoAttack();

        //①调用属性Setter方法注入
	   moAttack.setGeli(geli); 
	   moAttack.cityGateAsk();
   }
}

   和通过构造函数注入革离扮演者不同,在实例化MoAttack剧本时,并未指定任何扮演者,而是在实例化MoAttack后,在需要革离出场时,才调用其setGeli()方法注入扮演者。按照类似的方式,我们还可以分别为剧本中其他诸如梁王、巷淹中等角色提供注入的Setter方法,这样,导演就可以根据所拍剧段的不同,注入相应的角色了。

接口注入

   将调用类所有依赖注入的方法抽取到一个接口中,调用类通过实现该接口提供相应的注入方法。为了采取接口注入的方式,必须先声明一个ActorArrangable接口:
public interface ActorArrangable {
   void injectGeli(GeLi geli);
}

   然后,MoAttack实现ActorArrangable接口提供具体的实现:
代码清单3-7  MoAttack:通过接口方法注入革离扮演者
public class MoAttack implements ActorArrangable {
	private GeLi geli;
     //①实现接口方法
	public void injectGeli (GeLi geli) {  
		this.geli = geli;		
	}
	public void cityGateAsk() {
		geli.responseAsk("墨者革离");
	}
}

     Director通过ActorArrangable的injectGeli()方法完成扮演者的注入工作。
代码清单3-8  Director:通过接口方法注入革离扮演者
public class Director {
   public void direct(){
	   GeLi geli = new LiuDeHua();
	   MoAttack moAttack = new MoAttack();
	   moAttack. injectGeli (geli);
	   moAttack.cityGateAsk();
   }
}

    由于通过接口注入需要额外声明一个接口,增加了类的数目,而且它的效果和属性注入并无本质区别,因此我们不提倡采用这种方式。

通过容器完成依赖关系的注入

   虽然MoAttack和LiuDeHua实现了解耦,MoAttack无须关注角色实现类的实例化工作,但这些工作在代码中依然存在,只是转移到Director类中而已。假设某一制片人想改变这一局面,在选择某个剧本后,希望通过一个“海选”或者第三中介机构来选择导演、演员,让他们各司其职,那剧本、导演、演员就都实现解耦了。
   所谓媒体“海选”和第三方中介机构在程序领域即是一个第三方的容器,它帮助完成类的初始化与装配工作,让开发者从这些底层实现类的实例化、依赖关系装配等工作中脱离出来,专注于更有意义的业务逻辑开发工作。这无疑是一件令人向往的事情,Spring就是这样的一个容器,它通过配置文件或注解描述类和类之间的依赖关系,自动完成类的初始化和依赖注入的工作。下面是Spring配置文件的对以上实例进行配置的配置文件片断:
<?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:p="http://www.springframework.org/schema/p"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
        <!--①实现类实例化-->
   <bean id="geli" class="LiuDeHua"/>
   <bean id="moAttack" class="com.baobaotao.ioc.MoAttack" 
         p:geli-ref="geli"/><!--②通过geli-ref建立依赖关系-->
</beans>

   通过new XmlBeanFactory(“beans.xml”)等方式即可启动容器。在容器启动时,Spring根据配置文件的描述信息,自动实例化Bean并完成依赖关系的装配,从容器中即可返回准备就绪的Bean实例,后续可直接使用之。
   Spring为什么会有这种“神奇”的力量,仅凭一个简单的配置文件,就能魔法般地实例化并装配好程序所用的Bean呢?这种“神奇”的力量归功于Java语言本身的类反射功能。

   这些文章摘自于我的《Spring 4.x企业应用开发实战》的第3章,我将通过连载的方式,陆续在此发出。欢迎大家讨论。
  • 大小: 20.2 KB
  • 大小: 29.7 KB
  • 大小: 37.7 KB
分享到:
评论
19 楼 3258964 2017-12-18  
很到位,如果还不理解,可参考
http://www.srccode.cn/a_6.html 中的图片解释
18 楼 Pie_e 2017-08-31  
  
17 楼 瀚的some 2017-08-01  
我终于懂了
16 楼 duanxiaoyeah 2016-11-25  
说实话,把DI讲这么透彻,更可贵的还这么栩栩如生、通俗易懂、循序渐进、条理清晰,由衷的表示:佩服 + 感谢!
15 楼 RedPersimon 2016-07-19  
14 楼 myweimm 2016-05-23  
楼主说的相当的好啊,例子相当的不错,最欢看这种把理论,用很通俗的场景或白话讲解出来的文章,这也能说明作者的对理论的理解相当的深入
13 楼 xinzhong 2016-02-22  
不明觉厉。但还是进一步加深了理解,可惜我现在不用spring。谢谢分享!
12 楼 fran_maomao 2015-09-08  
感谢楼主分享  受益匪浅
11 楼 旋风魔帝 2015-08-10  
10 楼 飞鸟830413 2014-05-26  
精通Spring2.x-企业应用开发详解第三章内容。预知下回分解,请买本书看吧。不过楼主应该注明出处。
9 楼 mengfei1001 2013-04-03  
8 楼 a20071426 2012-09-24  
good!
7 楼 xiaoji123pt 2012-07-26  
 
6 楼 zlf3865072 2012-07-05  
23432
5 楼 shanliangdesishen9 2012-06-13  
谢谢分享  感觉受益匪浅
4 楼 gouerli 2012-06-08  
写的很不错,对于ioc的原理介绍的很清晰,比spring官方文档中好理解多了,对于接口注入的方式我倒是第一次听说,谢谢博主的分享!
3 楼 wh2006xtt 2012-05-16  
用到Spring的依赖注入的项目都完成了,对其原理还是晕的,读了此文明白些了。
O(∩_∩)O谢谢分享
2 楼 xautjzd 2012-04-28  
最近初学spring,看IoC看了半天没看懂,晕乎乎的,对其流程依然不解,但是看了此文章后便清楚多了,虽然还是没完全消化,但是起码有了一个大致的了解,感谢您的无私奉献!
1 楼 fount 2012-04-28  
spring使用有一段时间了,看看这系列的文章真是受益匪浅啊,谢谢分享。

相关推荐

    透透彻彻IoC(你没有理由不懂!) - stamen的程序员之路 - ITeye技术网站1

    【透透彻彻IoC(你没有理由不懂!)】这篇文章深入浅出地解释了IoC(控制反转)这一核心的编程概念,特别是在Spring框架中的应用。IoC是Spring容器的基础,它支持AOP(面向切面编程)和声明式事务处理等功能。然而,...

    Spring中IoC优点与缺点解析

    在上面的代码中,我们可以使用 IoC 容器来管理对象的创建和销毁,而不需要手动地创建和销毁对象。 缺点 1. 对象生成步骤变复杂:使用 IoC 容器可能会增加对象生成的步骤,因为需要配置 IoC 容器。 2. 效率损耗:...

    自己实现ioc实例demo

    在IT行业中,依赖注入(IOC,Inversion of Control)是一种设计模式,它使得应用程序的组件之间解耦,提高了代码的可测试性和可维护性。在这个“自己实现ioc实例demo”中,我们将探讨如何通过XPath解析XML文件来实现...

    springIoc实现原理

    **Spring Ioc 实现原理详解** Spring Ioc(Inversion of Control,控制反转)是Spring框架的核心特性之一,它改变了传统应用程序中对象的创建和管理方式。在传统的软件设计中,对象的创建和依赖关系的维护通常由...

    图片转IOC图标工具

    在软件开发过程中,图标是应用程序不可或缺的一部分,它不仅代表了应用的视觉形象,还为用户提供了一种快速识别和理解应用功能的方式。"图片转IOC图标工具"正是一款专为开发者设计的实用工具,旨在帮助他们将普通的...

    IoC小例子(了解一下IoC设计模式入门)

    现在,我们来看IoCTest这个压缩包中的文件,虽然没有具体的内容,但我们可以假设这是一个简单的IoC示例。通常,这样的测试文件可能会包含以下内容: 1. 一个接口或抽象类,定义了需要被依赖的服务。 2. 一个或多个...

    JavaEE Spring IoC入门

    总的来说,学习"JavaEE Spring IoC入门"意味着你需要理解Spring如何通过IoC管理对象,掌握XML配置文件的编写,以及如何在Web环境中使用Spring MVC进行开发。此外,还需要了解如何编写测试用例来验证Spring配置的有效...

    iocdemo.rar

    在本示例"iocdemo.rar"中,我们将探讨如何模仿Spring的IoC原理,通过XML配置和注解两种方式进行Bean的管理。 **控制反转(IoC)** IoC意味着应用程序的控制权由传统的程序流程控制转向了外部容器,即Spring框架。在...

    Android 进阶 教你打造 Android 中的 IOC 框架(上)

    在Android开发中,IOC(Inversion of Control,控制反转)框架是提高代码可维护性和可扩展性的重要工具。本文将探讨如何打造一个Android中的IOC框架,以实现组件间的解耦和更加灵活的代码结构。我们将主要关注两个...

    Spring-IOC实现

    在没有IOC的情况下,对象通常自行创建依赖。而在IOC中,这些依赖由外部容器(如Spring)提供,从而降低了代码间的耦合度。 2. **Spring容器** - Spring容器是Spring框架的核心,负责创建、配置和管理对象。主要有...

    spring Ioc容器配置

    spring Ioc容器配置 IOC容器数据源配置 &lt;!-- 配置数据源 --&gt; destroy-method="close"&gt; &lt;value&gt;org.gjt.mm.mysql.Driver &lt;value&gt;jdbc:mysql://localhost:3306/demo &lt;value&gt;root ...

    IOC练习事列

    **IOC(Inversion of Control)**,即控制反转,是一种设计模式,它的核心思想是将对象的创建和依赖关系的管理从应用代码中解耦出来,交给一个专门的容器来处理。这种模式使得代码更加灵活,降低了模块间的耦合度,...

    IoC 依赖注入 技术总结

    * 高度灵活性:IoC 依赖注入技术可以在不修改调用构件的情况下,重新指定被调用构件,从而实现软件系统的灵活配置。 * 高度可复用性:IoC 依赖注入技术可以将被调用构件实例化,并注入到调用构件之中,以实现软件...

    IOC详解IOC详解IOC详解IOC详解

    1. IOC type 0:没有使用IoC,对象自行创建依赖。如例子中的Girl自己创建Boy,这种方式会导致对象间的紧密耦合,不易于更换或共享,且对象生命周期难以管理。 2. IOC type 1:对象通过ServiceManager或者配置文件...

    JavaEE Spring IoC注解

    通过学习和理解这些代码,你可以更深入地了解Spring框架如何通过注解实现IoC,以及如何在实际项目中应用这些概念。实践是掌握知识的关键,所以建议你仔细研究这个测试案例,以便更好地掌握Spring的注解驱动IoC机制。

    雷赛IOC0640.rar

    标题中的“雷赛IOC0640.rar”指的是雷赛智能公司的一款名为IOC0640的工业控制产品,该产品通常用于自动化控制系统中,提供高效、稳定的输入/输出(I/O)管理。这个压缩包可能包含了关于该产品的详细资料和技术文档。...

    最新的ioc代码例子

    在Java开发中,IOC(Inversion of Control,控制反转)是一种设计原则,它将对象的创建和管理交给了外部容器,使得代码更加解耦和灵活。IoC的主要实现方式是依赖注入(Dependency Injection,DI)。在这个最新的IOC...

    spring ioc.jar

    《Spring IOC:构建灵活的控制反转容器》 Spring框架的核心在于其Inversion of Control(IOC)容器,也称为依赖注入(Dependency Injection)。IOC是软件设计中的一个重要概念,它改变了传统程序设计中对象间的依赖...

    利用Castle框架中IOC技术

    【 Castle 框架与 IOC 技术详解】 Castle框架是一个针对.NET平台的开源软件开发框架,它集合了多种成熟的应用技术,如ActiveRecord、AspectSharp、MonoRail以及MicroKernel/Windsor等。其中,MicroKernel是Castle的...

Global site tag (gtag.js) - Google Analytics