网站首页 > 技术文章 正文
很长一段时间里,我对控制反转和依赖注入这两个概念很模糊,闭上眼睛想一想,总有一种眩晕的感觉。但为了成为一名优秀的 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 { @Override public void saodi() { System.out.println("小二我在扫达摩院的地"); } public boolean isYijinjing() { // 星期三的时候小二和尚要练易筋经 return false; } }
小三类的代码修改如下所示:
public class Xiaosan implements Heshang { @Override public 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 只管使用就可以。
猜你喜欢
- 2024-10-18 MyBatisPlus又在搞事了!发布神器,一个依赖轻松搞定权限问题
- 2024-10-18 史上硬核!Jar 包依赖冲突排查思路和解决方法
- 2024-10-18 小白都能看懂的 Spring 源码揭秘之依赖注入(DI)源码分析
- 2024-10-18 Spring-bean的循环依赖以及解决方式
- 2024-10-18 Maven技术方案最全手册 maven详细教程
- 2024-10-18 JAVA编程规范之二方库依赖 java方法库
- 2024-10-18 Spring依赖注入原理分析 spring依赖注入作用
- 2024-10-18 简单2步实现maven打包java工程并将依赖jar输出到指定目录
- 2024-10-18 怎么进阶学好Java 如何用Spring解决循环依赖
- 2024-10-18 Spring 源码分析之 bean 依赖注入原理(注入属性)
你 发表评论:
欢迎- 11-19零基础学习!数据分析分类模型「支持向量机」
- 11-19机器学习 | 算法笔记(三)- 支持向量机算法以及代码实现
- 11-19我以前一直没有真正理解支持向量机,直到我画了一张图
- 11-19研一小姑娘分享机器学习之SVM支持向量机
- 11-19[机器学习] sklearn支持向量机
- 11-19支持向量机
- 11-19初探支持向量机:用大白话解释、原理详解、Python实现
- 11-19支持向量机的核函数
- 最近发表
- 标签列表
-
- oraclesql优化 (66)
- 类的加载机制 (75)
- feignclient (62)
- 一致性hash算法 (71)
- dockfile (66)
- 锁机制 (57)
- javaresponse (60)
- 查看hive版本 (59)
- phpworkerman (57)
- spark算子 (58)
- vue双向绑定的原理 (68)
- springbootget请求 (58)
- docker网络三种模式 (67)
- spring控制反转 (71)
- data:image/jpeg (69)
- base64 (69)
- java分页 (64)
- kibanadocker (60)
- qabstracttablemodel (62)
- java生成pdf文件 (69)
- deletelater (62)
- com.aspose.words (58)
- android.mk (62)
- qopengl (73)
- epoch_millis (61)
本文暂时没有评论,来添加一个吧(●'◡'●)