Java:控制反转(IoC)与依赖注入(DI)
很长一段时间里,我对控制反转和依赖注入这两个概念很模糊,闭上眼睛想一想,总有一种眩晕的感觉。但为了成为一名优秀的 Java 工程师,我花了一周的时间,彻底把它们搞清楚了。
01、紧耦合
在我们编码的过程中,通常都需要两个或者更多的类通过彼此的合作来实现业务逻辑,也就是说,某个对象需要获取与其合作对象的引用,如果这个获取的过程需要自己实现,代码的耦合度就会高,维护起来的成本就比较高。
我们来通过实战模拟一下。假如老王是少林寺的主持,他想让小二和尚去扫达摩院的地,代码可以这样实现。
小二类的代码如下所示:
public class Xiaoer {public void saodi() {System.out.println("小二我在扫达摩院的地");}
}
老王类的代码如下所示:
public class Laowang {public void mingling() {new Xiaoer().saodi();}
}
测试类的代码如下所示:
public class Test {public static void main(String[] args) {Laowang laowang = new Laowang();laowang.mingling();}}
Laowang 类的 mingling 方法中使用 new 关键字创建了一个 Xiaoer 类的对象——这种代码的耦合度就很高,维护起来的成本就很高,为什么这么说呢?
某一天,达摩院的地又脏了,老王主持想起了小二和尚,可小二和尚去练易筋经了,让谁去扫地呢,老王主持想起了小三和尚,于是 Laowang 类就不得不重新下一个新的命令,于是代码变成了这样:
public class Xiaosan {public void saodi() {System.out.println("小三我在扫达摩院的地");}
}
public class Laowang {public void mingling() {new Xiaoer().saodi();}public void mingling1() {new Xiaosan().saodi();}
}
假如小三和尚去挑水了,老王主持没准要下命令给小四和尚去扫达摩院的地。这样下去的话,Laowang 这个类会疯掉的。
老王主持觉得自己堂堂一届高僧,下个扫地的命令竟然这样麻烦,他觉得很不爽。
02、控制反转
我们得替老王主持想个办法对不对?
不如把这个扫地的差事交给老王的师弟老方吧,老方负责去叫小二和尚还是小三和尚还是小四和尚去执行老王主持的命令。代码可以这样实现。
定义一个扫地和尚的接口,代码如下所示:
public interface Heshang {void saodi();
}
小二类的代码修改如下所示:
public class Xiaoer implements Heshang {@Overridepublic void saodi() {System.out.println("小二我在扫达摩院的地"); }public boolean isYijinjing() {// 星期三的时候小二和尚要练易筋经return false;}
}
小三类的代码修改如下所示:
public class Xiaosan implements Heshang {@Overridepublic void saodi() {System.out.println("小三我在扫达摩院的地"); }
}
老方类的代码如下所示:
public class Laofang {public static Heshang getSaodiseng() {Xiaoer xiaoer = new Xiaoer();if (xiaoer.isYijinjing()) {return new Xiaosan();}return xiaoer;}
}
如果老方确认小二和尚在练易筋经,就叫小三和尚。
老王类的代码修改如下所示:
public class Laowang {public void mingling() {Laofang.getSaodiseng().saodi();}
}
测试类的代码不改变,如下所示:
public class Test {public static void main(String[] args) {Laowang laowang = new Laowang();laowang.mingling();}}
老王现在是不是省心多了,他只管下命令,该叫谁去扫达摩院的地由他师弟老方去负责。
我们替老王想的这个办法就叫控制反转(Inversion of Control,缩写为 IoC),它不是一种技术,而是一种思想——指导我们设计出松耦合的程序。
控制反转从词义上可以拆分为“控制”和“反转”,说到控制,就必须找出主语和宾语,谁控制了谁;说到反转,就必须知道正转是什么。
你看,在紧耦合的情况下,老王下命令的时候自己要通过 new 关键字创建依赖的对象(小二和尚或者小三和尚);而控制反转后,老王要找的扫地和尚由他师弟老方负责,也就是说控制权交给了老方,是不是反转了呢?
03、依赖注入
依赖注入(Dependency Injection,简称 DI)是实现控制反转的主要方式:在类 A 的实例创建过程中就创建了依赖的 B 对象,通过类型或名称来判断将不同的对象注入到不同的属性中。大概有 3 种具体的实现形式:
1)基于构造函数。实现特定参数的构造函数,在新建对象时传入所依赖类型的对象。
老王类的代码修改如下所示:
public class Laowang {private Heshang saodiseng;public Laowang(Heshang saodiseng) {this.saodiseng = saodiseng;}public void mingling() {this.saodiseng.saodi();}
}
测试类的代码修改如下所示:
public class Test {public static void main(String[] args) {Laowang laowang = new Laowang(new Xiaosan());laowang.mingling();}}
这时候,控制权掌握在测试类的手里,它决定派小二和尚还是小三和尚去执行老王的扫地命令。
2)基于 set 方法。实现特定属性的 public set 方法,让外部容器调用传入所依赖类型的对象。
老王类的代码修改如下所示:
public class Laowang {private Heshang saodiseng;public Heshang getSaodiseng() {return saodiseng;}public void setSaodiseng(Heshang saodiseng) {this.saodiseng = saodiseng;}public void mingling() {this.getSaodiseng().saodi();}
}
测试类的代码修改如下所示:
public class Test {public static void main(String[] args) {Laowang laowang = new Laowang();Xiaosan xiaosan = new Xiaosan();laowang.setSaodiseng(xiaosan);laowang.mingling();}}
这时候,控制权仍然掌握在测试类的手里,它决定派小二和尚还是小三和尚去执行老王的扫地命令。
3)基于接口。实现特定接口以供外部容器注入所依赖类型的对象,这种做法比较构造函数和 set 方法更为复杂,这里就此略过。
可能有人会把控制反转等同于依赖注入,但实际上它们有着本质上的不同:控制反转是一种思想,而依赖注入是实现控制反转的一种形式。
04、Spring 框架
当我们搞清楚控制反转和依赖注入的概念后,就可以顺带了解一下大名鼎鼎的 Spring 框架。控制反转是 Spring 框架的核心,贯穿始终。Spring 中依赖注入有两种实现方式:set 方式(传值方式)和构造器方式(引用方式)。
首先,我们需要在 pom.xml 文件中加入 Spring 的依赖项,代码如下所示:
<dependency><groupId>org.springframework</groupId><artifactId>spring-context-support</artifactId><version>4.3.2.RELEASE</version>
</dependency>
其次,我们将 Laowang 类修改为如下内容:
public class Laowang {private Heshang saodiseng;public Laowang(Heshang saodiseng) {this.saodiseng = saodiseng;}public void mingling() {this.saodiseng.saodi();}
}
然后,我们创建一个 Spring 的配置文件 application.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="laowang" class="com.cmower.java_demo.ioc.Laowang"><constructor-arg ref="saodiseng" /></bean><bean id="saodiseng" class="com.cmower.java_demo.ioc.Xiaosan" /></beans>
通过 元素配置了两个对象,一个老王主持,一个小三和尚,使用 元素将小三和尚作为老王主持的构造参数。
准备工作完成以后,我们来测试一下,代码示例如下:
public class Test {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");Laowang laowang = (Laowang) context.getBean("laowang");laowang.mingling();}}
你看,我们将控制权交给了 IoC 框架 Spring,这样也可以完美的解决代码耦合度较紧的问题。
05、最后
总结一下:
1)控制反转是一种在软件工程中解耦合的思想,把控制权交给了第三方,在运行的时候由第三方决定将具体的依赖对象“注入”到调用类的对象中。
2)依赖注入可以作为控制反转的一种实现方式,将实例变量传入到一个对象中去。
3)通过 IoC 框架,类 A 依赖类 B 的强耦合关系可以在运行时通过容器建立,也就是说把创建 B 实例的工作移交给容器,类 A 只管使用就可以。
欢迎关注「沉默王二」公众号,后台回复关键字「java」即可免费获取「Java 程序员进阶必读资料」。
Java:控制反转(IoC)与依赖注入(DI)相关推荐
- java-12:spring MVC - 控制反转IOC,依赖注入DI
学习spring框架之前,先理解几个概念: 1.第一部分:依赖倒置原则 2.第二部分:控制反转,控制反转容器(实例) 3.第三部分:控制反转,控制反转容器(全面理解,面试题) 综合性理解:控制反转(I ...
- 依赖倒置(DIP),控制反转(IoC)与依赖注入(DI)
DIP,IoC与DI概念解析 依赖倒置 DIP(Dependency Inversion Principle) DIP的两大原则: 1.高层模块不应该依赖于低层模块,二者都应该依赖于抽象. 2.抽象不 ...
- 控制反转IOC与依赖注入DI
为什么80%的码农都做不了架构师?>>> 1. IoC理论的背景 我们都知道,在采用面向对象方法设计的软件系统中,它的底层实现都是由N个对象组成的,所有的对象通过彼此的合作,最 ...
- 控制反转(IoC)与依赖注入(DI)详解
文章目录 什么是控制反转(IoC) 控制反转(IoC)有什么作用 控制反转(IoC)是怎么分类的 依赖注入 接口注入 Setter方法注入 构造器注入 依赖查找 上下文依赖查找(Contextuali ...
- 控制反转(Ioc)和依赖注入(DI)
控制反转IOC, 全称 "Inversion of Control".依赖注入DI, 全称 "Dependency Injection". 面向的问题:软件开发 ...
- 控制反转IOC、依赖注入DI的详细说明与举例
文章目录 引入 IOC介绍 IOC的实现 通过构造函数注入依赖 通过 setter 设值方法注入依赖 依赖注入容器 IOC优缺点 优点 缺点 阅读时忽略语言差异,参考了很多其他博主内容,参考博文在最后 ...
- Spring_01 spring容器、控制反转(IOC)、依赖注入(DI)
目录 1 什么是spring框架 2 spring框架的特点 3 spring容器 3.1 什么是spring容器 3.2 spring容器创建对象的编程步骤 3.4 spring容器创建对象的方式 ...
- 控制反转 IOC 与依赖注入 DI
引言 简单总结和巩固一下spring的核心原理--IOC和DI的概念,为什么IOC要叫控制反转?IOC和DI的关系是怎样的? 一.IOC 控制反转 初学者可能很好奇,为什么spring framewo ...
- [教程]控制反转(IoC)与依赖注入(DI)
来源: http://zhangjunhd.blog.51cto.com/113473/126530/ 挺简单的,说的也很清楚 ※IoC/DI 依赖Java的反射机制 1.控制反转(Inversion ...
- (转载)控制反转(IoC)与依赖注入(DI)
http://zhangjunhd.blog.51cto.com/113473/126530/ 转载于:https://www.cnblogs.com/eecs2016/articles/741709 ...
最新文章
- 好吧,关于nginx
- 解决MyBatis的报错 There is no getter for property named ‘*‘ in ‘class java.lang.String‘
- 自定义服务器控件(扩展现有 Web 控件)
- AAAI2019 | 腾讯AI Lab详解自然语言处理领域三大研究方向及入选论文
- java put set_Java PutItemRequest.setItem方法代码示例
- Electron 打包Mac安装包代码签名问题解决方案Could not get code signature for running application
- 知道python_看完这篇文章,你就知道Python是什么?
- php三步运算法,小技巧----“三步翻转法”
- SQL注射技术总结文档
- C++fseek函数
- Error:A problem occurred configuring project ':app'. failed to find target with hash string 'andro
- Linux 系统进程守护工具 cesi + superviosr
- iOS POS之3DES加密
- python的post请求中加参数_Python 模拟post请求
- 合并报表编制采用的理论_合并报表编制理论知识(必读)详解
- Arcgis pro中Arcpy对Excel文件进行克里金插值
- 【分享】轻量级Linux + docker
- 《神经网络与深度学习》算法伪代码汇总
- 刘未鹏博客 http://mindhacks.cn/
- 论文SlimYOLOv3: Narrower, Faster and Better for Real-Time UAV Applications