摘要:对象之间耦合度过高的系统,必然会出现牵一发而动全身的情形。控制被反转之后,获得依赖对象的过程由自身管理变为了由容器主动注入。于是,他给控制反转取了一个更合适的名字叫做依赖注入。
Spring还可以这么学--IoC(控制反转) / DI(依赖注入)理解
声明:文章的前三部分参考博文:https://www.cnblogs.com/Nouno...
这篇文章首发是在我的个人微信订阅号每天学编程,关注我的微信订阅号查看更多文章。
我们都知道,在采用面向对象方法设计的软件系统中,它的底层实现都是由N个对象组成的,所有的对象通过彼此的合作,最终实现系统的业务逻辑。
图1:软件系统中耦合的对象
如果我们打开机械式手表的后盖,就会看到与上面类似的情形,各个齿轮分别带动时针、分针和秒 针顺时针旋转,从而在表盘上产生正确的时间。图1中描述的就是这样的一个齿轮组,它拥有多个独立的齿轮,这些齿轮相互啮合在一起,协同工作,共同完成某项 任务。我们可以看到,在这样的齿轮组中,如果有一个齿轮出了问题,就可能会影响到整个齿轮组的正常运转。
齿轮组中齿轮之间的啮合关系,与软件系统中对象之间的耦合关系非常相似。对象之间的耦合关系是无法避免的,也是必要的,这是协同工作的基础。现在,伴随着 工业级应用的规模越来越庞大,对象之间的依赖关系也越来越复杂,经常会出现对象之间的多重依赖性关系,因此,架构师和设计师对于系统的分析和设计,将面临 更大的挑战。对象之间耦合度过高的系统,必然会出现牵一发而动全身的情形。
图2:对象之间复杂的依赖关系
耦合关系不仅会出现在对象与对象之间,也会出现在软件系统的各模块之间,以及软件系统和硬件系统之间。如何降低系统之间、模块之间和对象之间的耦合度,是软件工程永远追求的目标之一。为了解决对象之间的耦合度过高的问题,软件专家Michael Mattson提出了IOC理论,用来实现对象之间的“解耦”,目前这个理论已经被成功地应用到实践当中,很多的J2EE项目均采用了IOC框架产品Spring。
2. 什么是控制反转(IoC)IOC是Inversion of Control的缩写,多数书籍翻译成“控制反转”,还有些书籍翻译成为“控制反向”或者“控制倒置”。
1996年,Michael Mattson在一篇有关探讨面向对象框架的文章中,首先提出了IOC 这个概念。对于面向对象设计及编程的基本思想,前面我们已经讲了很多了,不再赘述,简单来说就是把复杂系统分解成相互合作的对象,这些对象类通过封装以 后,内部实现对外部是透明的,从而降低了解决问题的复杂度,而且可以灵活地被重用和扩展。IOC理论提出的观点大体是这样的:借助于“第三方”实现具有依 赖关系的对象之间的解耦,如下图:
图3:IOC解耦过程
大家看到了吧,由于引进了中间位置的“第三方”,也就是IOC容器,使得A、B、C、D这4个对象没有了耦合关系,齿轮之间的传动全部依靠“第三 方”了,全部对象的控制权全部上缴给“第三方”IOC容器,所以,IOC容器成了整个系统的关键核心,它起到了一种类似“粘合剂”的作用,把系统中的所有 对象粘合在一起发挥作用,如果没有这个“粘合剂”,对象与对象之间会彼此失去联系,这就是有人把IOC容器比喻成“粘合剂”的由来。
我们再来做个试验:把上图中间的IOC容器拿掉,然后再来看看这套系统:
图4:拿掉IoC容器后的系统
我们现在看到的画面,就是我们要实现整个系统所需要完成的全部内容。这时候,A、B、C、D这4个对象之间已经没有了耦合关系,彼此毫无联系,这样 的话,当你在实现A的时候,根本无须再去考虑B、C和D了,对象之间的依赖关系已经降低到了最低程度。所以,如果真能实现IOC容器,对于系统开发而言, 这将是一件多么美好的事情,参与开发的每一成员只要实现自己的类就可以了,跟别人没有任何关系!
我们再来看看,控制反转(IOC)到底为什么要起这么个名字?我们来对比一下:
软件系统在没有引入IOC容器之前,如图1所示,对象A依赖于对象B,那么对象A在初始化或者运行到某一点的时候,自己必须主动去创建对象B或者使用已经创建的对象B。无论是创建还是使用对象B,控制权都在自己手上。
软件系统在引入IOC容器之后,这种情形就完全改变了,如图3所示,由于IOC容器的加入,对象A与对象B之间失去了直接联系,所以,当对象A运行到需要对象B的时候,IOC容器会主动创建一个对象B注入到对象A需要的地方。
通过前后的对比,我们不难看出来:对象A获得依赖对象B的过程,由主动行为变为了被动行为,控制权颠倒过来了,这就是“控制反转”这个名称的由来。
2004年,Martin Fowler探讨了同一个问题,既然IOC是控制反转,那么到底是“哪些方面的控制被反转了呢?”,经过详细地分析和论证后,他得出了答案:“获得依赖对 象的过程被反转了”。控制被反转之后,获得依赖对象的过程由自身管理变为了由IOC容器主动注入。于是,他给“控制反转”取了一个更合适的名字叫做“依赖 注入(Dependency Injection)”。他的这个答案,实际上给出了实现IOC的方法:注入。所谓依赖注入,就是由IOC容器在运行期间,动态地将某种依赖关系注入到对 象之中。
所以,依赖注入(DI)和控制反转(IOC)是从不同的角度的描述的同一件事情,就是指通过引入IOC容器,利用依赖关系注入的方式,实现对象之间的解耦。
我们举一个生活中的例子,来帮助理解依赖注入的过程。大家对USB接口和USB设备应该都很熟悉吧,USB为我们使用电脑提供了很大的方便,现在有很多的外部设备都支持USB接口。
图5:USB接口和USB设备
现在,我们利用电脑主机和USB接口来实现一个任务:从外部USB设备读取一个文件。
电脑主机读取文件的时候,它一点也不会关心USB接口上连接的是什么外部设备,而且它确实也无须知道。它的任务就是读取USB接口,挂接的外部设备只要符 合USB接口标准即可。所以,如果我给电脑主机连接上一个U盘,那么主机就从U盘上读取文件;如果我给电脑主机连接上一个外置硬盘,那么电脑主机就从外置 硬盘上读取文件。挂接外部设备的权力由我作主,即控制权归我,至于USB接口挂接的是什么设备,电脑主机是决定不了,它只能被动的接受。电脑主机需要外部 设备的时候,根本不用它告诉我,我就会主动帮它挂上它想要的外部设备,你看我的服务是多么的到位。这就是我们生活中常见的一个依赖注入的例子。在这个过程 中,我就起到了IOC容器的作用。
通过这个例子,依赖注入的思路已经非常清楚:当电脑主机读取文件的时候,我就把它所要依赖的外部设备,帮他挂接上。整个外部设备注入的过程和一个被依赖的对象在系统运行时被注入另外一个对象内部的过程完全一样。
我们把依赖注入应用到软件系统中,再来描述一下这个过程:
对象A依赖于对象B,当对象 A需要用到对象B的时候,IOC容器就会立即创建一个对象B送给对象A。IOC容器就是一个对象制造工厂,你需要什么,它会给你送去,你直接使用就行了, 而再也不用去关心你所用的东西是如何制成的,也不用关心最后是怎么被销毁的,这一切全部由IOC容器包办。
在传统的实现中,由程序内部代码来控制组件之间的关系。我们经常使用new关键字来实现两个组件之间关系的组合,这种实现方式会造成组件之间耦合。IOC 很好地解决了该问题,它将实现组件间关系从程序内部提到外部容器,也就是说由容器在运行期将组件间的某种依赖关系动态注入组件中。
最后,我们用两个实例来看DI的注入方式,代码也是十分简单的!
通过构造函数注入ClassB 类通过类构造函数被注入到 ClassA 类中
ClassB.java文件
package com.wangc; public class ClassB { public ClassB() { System.out.println("hello,我在ClassB的构造器里!"); } public void say() { System.out.println("hello,我是ClassB!"); } }
ClassA.java文件
package com.wangc; public class ClassA { private ClassB classB; public ClassA(ClassB classB) { System.out.println("hello,我在ClassA的构造器里!"); this.classB = classB; } public void sayHello() { classB.say(); } }
Beans.xml文件
MainApp.java文件
package com.wangc; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MainApp { public static void main(String[] args) { ApplicationContext context =new ClassPathXmlApplicationContext("Beans.xml"); ClassA classA = (ClassA) context.getBean("classA"); classA.sayHello(); } }
运行结果:
hello,我在ClassB的构造器里! hello,我在ClassA的构造器里! hello,我是ClassB!通过setter方法注入
当容器调用一个无参的构造函数或一个无参的静态 factory 方法来初始化你的 bean 后,通过容器在你的 bean 上调用设值函数,基于设值函数的 DI 就完成了。 将上面例子中的ClassA.java文件和Beans.xml文件作如下改动,其他文件不变。
ClassA.java文件
package com.wangc; public class ClassA { private ClassB classB; public ClassB getClassB() { return classB; } public void setClassB(ClassB classB) { System.out.println("hello,我在setClassB里!"); this.classB = classB; } public void sayHello() { classB.say(); } }
Beans.xml文件
运行结果:
hello,我在ClassB的构造器里! hello,我在setClassB里! hello,我是ClassB!
QQ学习交流群:713479727
微信学习交流群:微信群加入方式【公众号下方菜单栏-->学习资源-->微信群】
微信公众号:EverydayCoding 或扫描下方二维码
欢迎大家加入。。。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/69323.html
摘要:使用的好处知乎的回答不用自己组装,拿来就用。统一配置,便于修改。 前言 只有光头才能变强 回顾前面: 给女朋友讲解什么是代理模式 包装模式就是这么简单啦 单例模式你会几种写法? 工厂模式理解了没有? 在刷Spring书籍的时候花了点时间去学习了单例模式和工厂模式,总的来说还是非常值得的! 本来想的是刷完《Spring 实战 (第4版)》和《精通Spring4.x 企业应用开发实战》...
摘要:学习总结学习整理的一些笔记,很简单。大部分认为和只是不同的叫法而已。依赖注入的两种方式和注解使用注释驱动的功能源码剖析 Spring IoC学习总结 学习spring Ioc整理的一些笔记,很简单。分享给大家。 IoC 基本概念 在这之前,我们先记住一句话。好莱坞原则:Dont call us, we will call you.其实这句话很恰当地形容了反转的意味;Ioc, Inve...
摘要:模块负责的所有面向切面的功能。总结的统一管理,降低了对象之间的耦合对主流的框架提供了很好的集成支持提供众多组件,事务管理,等具有高度可开放性,开发者可以自由选择部分或全部主要使用工厂模式和代理模式。 聊完了Spring框架中最重要的两种设计模式,我们来看一下Spring框架的模块和结构图。 Spring框架的结构 下图是Spring官方给出的Spring框架的结构图。 showImg(...
摘要:的两大核心机制是控制反转和面向切面编程,对于初学者来讲,搞清楚这两个核心机制就掌握了的基本应用。配置对象张三添加标签对应属性名,是属性的值。若包含特殊字符,比如张三,使用张三进行配置,如下所示。 前言 对于任何一个 Java 开发人员,Spring 的大名一定如雷贯耳,在行业中可谓是无人不知、无人不晓,说它是 Java 领域第一框架毫不为过。 showImg(https://segme...
阅读 820·2023-04-26 00:11
阅读 2615·2021-11-04 16:13
阅读 2077·2021-09-09 09:33
阅读 1451·2021-08-20 09:35
阅读 3789·2021-08-09 13:42
阅读 3578·2019-08-30 15:55
阅读 981·2019-08-30 15:55
阅读 2175·2019-08-30 13:55