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

[b][size=x-large]通过实例理解IoC的概念[/size][/b]

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

public class MoAttack {   public void cityGateAsk(){        //①演员直接侵入剧本       LiuDeHua ldh = new LiuDeHua();       ldh.responseAsk("墨者革离!");   }}

我们会发现以上剧本在①处,作为具体角色饰演者的刘德华直接侵入到剧本中,使剧本和演员直接耦合在一起(图3-1)。
[img]http://dl.iteye.com/upload/attachment/0066/7636/f7676f68-e41e-3883-a37f-046ae379c321.jpg[/img]
一个明智的编剧在剧情创作时应围绕故事的角色进行,而不应考虑角色的具体饰演者,这样才可能在剧本投拍时自由地遴选任何适合的演员,而非绑定在刘德华一人身上。通过以上的分析,我们知道需要为该剧本主人公革离定义一个接口:
代码清单3-2 MoAttack:引入剧本角色

public class MoAttack {   public void cityGateAsk()   {        //①引入革离角色接口      GeLi geli = new LiuDeHua(); 

        //②通过接口开展剧情      geli.responseAsk("墨者革离!");     }}

在①处引入了剧本的角色——革离,剧本的情节通过角色展开,在拍摄时角色由演员饰演,如②处所示。因此墨攻、革离、刘德华三者的类图关系如图 3 2所示:
[img]http://dl.iteye.com/upload/attachment/0066/7640/14d800d6-3505-3ca3-b21b-01194aaf0a83.jpg[/img]
可是,从图3 2中,我们可以看出MoAttack同时依赖于GeLi接口和LiuDeHua类,并没有达到我们所期望的剧本仅依赖于角色的目的。但是角色最终必须通过具体的演员才能完成拍摄,如何让LiuDeHua和剧本无关而又能完成GeLi的具体动作呢?当然是在影片投拍时,导演将LiuDeHua安排在GeLi的角色上,导演将剧本、角色、饰演者装配起来(图3-3)。
[img]http://dl.iteye.com/upload/attachment/0066/7642/24018187-7df1-3657-8464-545eabdf456a.jpg[/img]
通过引入导演,使剧本和具体饰演者解耦了。对应到软件中,导演像是一个装配器,安排演员表演具体的角色。
现在我们可以反过来讲解IoC的概念了。IoC(Inverse of Control)的字面意思是控制反转,它包括两个内容:
[list]
[*]其一是控制
[*]其二是反转
[/list]
[b][color=blue]那到底是什么东西的“控制”被“反转”了呢[/color][/b]?对应到前面的例子,“控制”是指选择GeLi角色扮演者的控制权;“反转”是指这种控制权从《墨攻》剧本中移除,转交到导演的手中。对于软件来说,即是某一接口具体实现类的选择控制权从调用类中移除,转交给第三方决定。
因为IoC确实不够开门见山,因此业界曾进行了广泛的讨论,最终软件界的泰斗级人物Martin Fowler提出了DI(依赖注入:Dependency Injection)的概念用以代替IoC,即让调用类对某一接口实现类的依赖关系由第三方(容器或协作类)注入,以移除调用类对某一接口实现类的依赖。“依赖注入”这个名词显然比“控制反转”直接明了、易于理解。

[b][size=x-large]IoC的类型[/size][/b]

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

[b][size=large]构造函数注入[/size][/b]

在构造函数注入中,我们通过调用类的构造函数,将接口实现类通过构造函数变量传入,如代码清单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();   }}

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

[b][size=large]属性注入[/size][/b]

有时,导演会发现,虽然革离是影片《墨攻》的第一主角,但并非每个场景都需要革离的出现,在这种情况下通过构造函数注入相当于每时每刻都在革离的饰演者在场,可见并不妥当,这时可以考虑使用属性注入。属性注入可以有选择地通过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方法,这样,导演就可以根据所拍剧段的不同,注入相应的角色了。

[b][size=large]接口注入[/size][/b]

将调用类所有依赖注入的方法抽取到一个接口中,调用类通过实现该接口提供相应的注入方法。为了采取接口注入的方式,必须先声明一个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();   }}

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

[b][size=x-large]通过容器完成依赖关系的注入[/size][/b]

虽然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语言本身的类反射功能。

这些文章摘自于我的[b][color=red]《Spring 4.x企业应用开发实战》[/color][/b]的第3章,我将通过连载的方式,陆续在此发出。欢迎大家讨论。

透透彻彻IoC(你没有理由不懂!)相关推荐

  1. [Java]Spring Ioc讲解,不怕你不懂

    引述 :IoC(控制反转:Inverse of Control)是Spring容器的内核,AOP.声明式事务等功能在此基础上开花结果.但是IoC这个重要的概念却比较晦涩隐讳,不容易让人望文生义,这不能 ...

  2. IoC框架,依赖注入

    IOC的机制是:---处理类之间和接口之间或类与接口之间关联关系,根据好莱坞原则,调用着与被调用者的主次关系,实现开关的原则.类之间可以很好(甚至)可以完全避免耦合,一个类只负责自己逻辑功能代码,如果 ...

  3. Latex-加通讯作者的小信封标记

    原创 Latex中通信作者上标小信封如何生成? 2019年07月09日 16:17:14 tongle.Wang 阅读数 243更多 分类专栏: 杂项 版权声明:本文为博主原创文章,遵循 CC 4.0 ...

  4. 给还没毕业的同学的衷心告诫[转]

    今年毕业,上的不是什么好大学,名不见经传的二本院校.在这里哥作为一个师哥,给弟弟妹妹们一些告诫,如果你有幸看到了,代表咱哥们有缘,没白费我一片苦心,如果你听取了,代表你有福气能少走写弯路.就算你爹是李 ...

  5. 系统分析师电子版_3个月,我从待业青年变成数据分析师,月薪15000!

    大家好,我是Joeicy,一枚2020年的毕业生.相信很多同学和我一样,被年初的疫情黑天鹅打乱求职阵脚.企业岗位缩招,外加我毫无实习经历,在求职路上竞争优势几乎为0. 幸好!我这人天生乐观,在一番心态 ...

  6. 季羡林谈人生——意义和价值

    人生 在一个"人生漫谈"的专栏中,首先谈一谈人生,似乎是理所当然的,未可厚非的. 而且我认为,对于我来说,这个题目也并不难写.我已经到了望九之年,在人生中已经滚了八十多个春秋了.一 ...

  7. 人生的意义与价值——季羡林

    当我还是一个青年大学生的时候,报刊上曾刮起一阵讨论人生的意义与价值的微风,文章写了一些,议论也发表了一通.我看过一些文章,但自己并没有参加进去.原因是,有的文章不知所云,我看不懂.更重要的是,我认为这 ...

  8. 智慧书——永恒的处世经典(正文 61-120)

    61.在崇高事业上出类拔萃 在完美特质中,此为最罕见者.凡英雄豪杰,必有某种高尚品质.平庸之辈绝难赢得掌声.在高尚事业上出类拔革可使我们不同于凡夫俗子,而成为卓绝超拔的人物.在低贱的职业上做得再出色也 ...

  9. 我为什么退掉iPad Pro买华为matepad-鸿蒙os yyds

    开头语:买平板电脑前一定要想清楚自己的需求是什么,切勿创造需求 首先,我是一个穷苦学生党,这个iPad我是足足想了一年,知道今年才攒够钱,于是在苹果教育优惠前抢跑买了iPad Pro 2021,买之前 ...

最新文章

  1. 单片机学习从入门到入土?这3个关键点导致!
  2. HTTP状态码和支持的方法
  3. js img图片加载失败,重新加载+断网检查
  4. 二阶振荡衰减 matlab,基于Matlab/Simulink的二阶控制系统仿真研究
  5. dijkstra--非负权值的单源最短路径STL实现(邻接表+优先队列) (带路径)
  6. Linux 容器 vs 虚拟机 —— 谁更胜一筹
  7. SpringCloud的Hystrix(五) Hystrix机制
  8. OFD文件结构--OFD.xml
  9. 加密软件不能安装软件
  10. eclipse换炫酷主题
  11. SHELL脚本获取某天的上一周日期(星期一为第一天)
  12. 关闭Windows系统的应用程序或窗口的快捷键有哪些?
  13. 论文那些事儿:《Unpaired Image-to-Image Translation using Cycle-Consistent Adversarial Networks》
  14. orthWind 数据库结构说明
  15. 【A40i-Android7.1】---编译报错:No Jack server running. Try ‘jack-admin start-server
  16. php fav ico_前5名:失败原因,Fav桌面环境,代码游戏玩家评论等
  17. 那个“支付宝锦鲤”女孩,现在变得怎么样了?你肯定想象不到
  18. 白帽子讲WEB安全读书笔记(慢慢更新)
  19. abb机械手故障代码20082_ABB工业机器人常见的故障和如何处理这些故障的详细
  20. StateFlow用法汇总篇

热门文章

  1. 编码器 协议不公开_公开编码的3种后果
  2. DTU基础知识普及手册
  3. java判断工作日_java 查询指定月份的工作日(不包括法定节假日)
  4. 程序员的十层楼 11层(上帝)
  5. Taichi安装与应用
  6. 职工信息管理系统(链表版)
  7. 微信 群相册服务器,微信也有群相册!用这个小程序,轻松优雅收集聚会合影...
  8. JAVA Date 工具类 常用
  9. C++动态分配内存空间
  10. 桥接模式 和 享元模式 介绍