Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >Java:控制反转(IoC)与依赖注入(DI)【精】

Java:控制反转(IoC)与依赖注入(DI)【精】

作者头像
ZhangXianSheng
发布于 2019-09-03 12:07:23
发布于 2019-09-03 12:07:23
80400
代码可运行
举报
运行总次数:0
代码可运行

很长一段时间里,我对控制反转和依赖注入这两个概念很模糊,闭上眼睛想一想,总有一种眩晕的感觉。但为了成为一名优秀的 Java 工程师,我花了一周的时间,彻底把它们搞清楚了。

01、紧耦合

在我们编码的过程中,通常都需要两个或者更多的类通过彼此的合作来实现业务逻辑,也就是说,某个对象需要获取与其合作对象的引用,如果这个获取的过程需要自己实现,代码的耦合度就会高,维护起来的成本就比较高。

我们来通过实战模拟一下。假如老王是少林寺的主持,他想让小二和尚去扫达摩院的地,代码可以这样实现。

小二类的代码如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Xiaoer {
    public void saodi() {
        System.out.println("小二我在扫达摩院的地");
    }
}

老王类的代码如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Laowang {
    public void mingling() {
        new Xiaoer().saodi();
    }
}

测试类的代码如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Test {

    public static void main(String[] args) {
        Laowang laowang = new Laowang();
        laowang.mingling();
    }

}

Laowang 类的 mingling 方法中使用 new 关键字创建了一个 Xiaoer 类的对象——这种代码的耦合度就很高,维护起来的成本就很高,为什么这么说呢?

某一天,达摩院的地又脏了,老王主持想起了小二和尚,可小二和尚去练易筋经了,让谁去扫地呢,老王主持想起了小三和尚,于是 Laowang 类就不得不重新下一个新的命令,于是代码变成了这样:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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、控制反转

我们得替老王主持想个办法对不对?

不如把这个扫地的差事交给老王的师弟老方吧,老方负责去叫小二和尚还是小三和尚还是小四和尚去执行老王主持的命令。代码可以这样实现。

定义一个扫地和尚的接口,代码如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public interface Heshang {
    void saodi();
}

小二类的代码修改如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Xiaoer implements Heshang {

    @Override
    public void saodi() {
        System.out.println("小二我在扫达摩院的地");        
    }

    public boolean isYijinjing() {
        // 星期三的时候小二和尚要练易筋经
        return false;
    }
}

小三类的代码修改如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Xiaosan implements Heshang {

    @Override
    public void saodi() {
        System.out.println("小三我在扫达摩院的地");        
    }
}

老方类的代码如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Laofang {
    public static Heshang getSaodiseng() {
        Xiaoer xiaoer = new Xiaoer();
        if (xiaoer.isYijinjing()) {
            return new Xiaosan();
        }
        return xiaoer;
    }
}

如果老方确认小二和尚在练易筋经,就叫小三和尚。

老王类的代码修改如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Laowang {
    public void mingling() {
        Laofang.getSaodiseng().saodi();
    }
}

测试类的代码不改变,如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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)基于构造函数。实现特定参数的构造函数,在新建对象时传入所依赖类型的对象。

老王类的代码修改如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Laowang {
    private Heshang saodiseng;
    
    public Laowang(Heshang saodiseng) {
        this.saodiseng = saodiseng;
    }
    public void mingling() {
       this.saodiseng.saodi();
    }
}

测试类的代码修改如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Test {

    public static void main(String[] args) {
        Laowang laowang = new Laowang(new Xiaosan());
        laowang.mingling();
    }

}

这时候,控制权掌握在测试类的手里,它决定派小二和尚还是小三和尚去执行老王的扫地命令。

2)基于 set 方法。实现特定属性的 public set 方法,让外部容器调用传入所依赖类型的对象。

老王类的代码修改如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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();
    }
}

测试类的代码修改如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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 的依赖项,代码如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
    <version>4.3.2.RELEASE</version>
</dependency>

其次,我们将 Laowang 类修改为如下内容:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Laowang {
    private Heshang saodiseng;
    
    public Laowang(Heshang saodiseng) {
        this.saodiseng = saodiseng;
    }
    public void mingling() {
       this.saodiseng.saodi();
    }
}

然后,我们创建一个 Spring 的配置文件 application.xml,内容如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<?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>

通过 <bean> 元素配置了两个对象,一个老王主持,一个小三和尚,使用 <constructor-arg> 元素将小三和尚作为老王主持的构造参数。

准备工作完成以后,我们来测试一下,代码示例如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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 只管使用就可以。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
硬核!如何全面系统地自学Java
我今年 XX 岁(调皮),使用 Java 开发将近 15 年了,目前虽然不在技术一线,但仍然和 Java 形影相伴,如影随形,每天都在相爱相杀。
沉默王二
2021/04/02
8000
IOC控制反转与DI依赖注入
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
全栈程序员站长
2022/08/31
1990
IOC控制反转与DI依赖注入
spring的ioc实现原理_ioc控制反转和di依赖注入
大家好,我是架构君,一个会写代码吟诗的架构师。今天说一说spring的ioc实现原理_ioc控制反转和di依赖注入,希望能够帮助大家进步!!!
Java架构师必看
2022/07/06
4760
spring的ioc实现原理_ioc控制反转和di依赖注入
Spring控制反转与依赖注入
学习Spring框架,它的核心就是IoC容器。要掌握Spring框架,就必须要理解控制反转的思想以及依赖注入的实现方式。下面,我们将围绕下面几个问题来探讨控制反转与依赖注入的关系以及在Spring中如何应用。
用户3467126
2019/08/12
6480
IOC控制反转 + DI依赖注入
Spring中的IOC一种思想,两种实现方式IOC (Inversion of Control):控制反转,是一种概念和思想,指由Spring容器完成对象创建和依赖注入核心业务:(a)对象的创建 (b)依赖的注入2种实现方式基于xml实现IOC基于注解实现IOC基于xml的IOC在前3篇Spring博客中简单探讨过了,后面将探讨基于注解的IOC基于注解的IOCDI (Dependency Injection):基于注解的IOC被称为DI,即依赖注入, 是IOC思想的一种具体实现方式根据IOC的核心业务即:(
愿天堂没有BUG
2022/09/28
2870
Spring之IoC(控制反转)与DI(依赖注入)
本博客将深入探讨控制反转(IoC)和依赖注入(DI)的概念与原理。我们将讨论如何通过XML和注解配置IoC容器,解释Bean的生命周期和作用域,并提供代码示例和注释。此外,还将强调注意事项,并在最后总结所讨论的内容。
默 语
2024/11/20
1470
.NET IoC模式依赖反转(DIP)、控制反转(Ioc)、依赖注入(DI)
依赖倒置(Dependency Inversion Principle,缩写DIP)是面向对象六大基本原则之一。他是指一种特定的的解耦形式,使得高层次的模块不依赖低层次的模块的实现细节,依赖关系被颠倒(反转),从而使得低层次模块依赖于高层次模块的需求抽象.
HueiFeng
2020/05/13
1.2K0
Spring系列第2篇:控制反转(IoC)与依赖注入(DI),晦涩难懂么?
Spring中有3个核心的概念:控制反转(Ioc)、依赖注入(DI)、面向切面编程(AOP),spring中其他的技术都是依靠3个核心的技术建立起来的,所以玩spring需要先对这3个概念有个深入的理解。
路人甲Java
2020/02/18
6340
到底什么是控制反转(IOC)和依赖注入(DI)
在编程中,当我们用到一个对象时,首先需要主动创建它,但是在大型项目中,大家分工合作,也许我们要用到的对象对应的类还没有被编写出来,我们肯定没法办new出来,这个时候就有了Spring框架,对象交给Spring来创建(我们不关心也没有能力关心是否已经有了对应的类和Spring是否能够创建我们需要的对象),我们直接从Spring容器中获取对象使用即可,此前对象是由我们主动创建的,我们具有控制权,但使用Spring框架之后我们将创建对象的权力交给了Spring,也就意味着我们对对象的控制权反转给了Spring,这就是控制反转;
訾博ZiBo
2025/01/06
530
使用 IOC 控制反转和 DI 依赖注入的意义
其实我的标题没写对,这个话题我是聊不下去的。 本文只和小伙伴聊聊为什么使用容器注入,优缺点是什么。我通过问问题的方式让小伙伴了解这么做的意义
林德熙
2020/07/28
9210
使用 IOC 控制反转和 DI 依赖注入的意义
Spring框架IoC控制反转
Spring是与2003年兴起的一个轻量级的Java开发框架,它是为了解决企业应用开发的复杂性而创建的。Spring的核心是控制反转(IOC)和面向切面编程(AOP)。Spring是可以在Java SE/EE中使用的轻量级开源框架。
久绊A
2024/03/01
1070
控制反转与依赖注入
控制反转是Spring框架的核心思想,也是因为Spring的关系这个模式为大众所知晓。
李鸿坤
2020/07/20
4990
控制反转与依赖注入
PHP 依赖注入(DI)和控制反转(IoC)
Ioc 容器维护 binding 数组记录 bind 方法传入的键值对如:log=>FileLog, user=>User
友儿
2022/09/11
6770
轻松理解.NET控制反转和依赖注入
在软件工程领域,特别是在 C# 和 .NET 的上下文中,控制反转(Inversion of Control,IoC)和依赖注入(Dependency Injection,DI)是增强代码模块化、可测试性和可维护性的基本原则。这些范式允许开发人员通过将依赖关系的创建和管理与业务逻辑分离,构建松耦合、灵活的应用程序。
郑子铭
2024/07/12
2580
轻松理解.NET控制反转和依赖注入
依赖注入和控制反转
学习过Spring框架的人一定都会听过Spring的IoC(控制反转) 、DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC 、DI这两个概念是模糊不清的,是很难理解的,今天和大家分享网上的一些技术大牛们对Spring框架的IOC的理解以及谈谈我对Spring Ioc的理解。
AlbertZhang
2020/11/05
8850
Spring学习笔记 | 控制反转IoC与依赖注入DI
IoC 是Spring框架最核心的特性之一。在IoC模式下,对象(组件)的创建和管理不再由对象自己负责,而是交给了IoC容器。对象只需要声明自己的依赖,IoC容器会在运行时自动将依赖注入到对象中。
windealli
2024/04/30
3760
Spring学习笔记 | 控制反转IoC与依赖注入DI
springboot第7集:控制反转(IoC)与依赖注入(DI)
Spring是一个非常流行的Java应用程序框架,它是基于IoC(Inversion of Control)和DI(Dependency Injection)的。在这篇文章中,我们将详细介绍IoC和DI的概念,以及如何在Spring中使用它们。
达达前端
2023/10/08
4790
深入理解 Spring IoC 和 DI:掌握控制反转和依赖注入的精髓
控制反转是软件工程中的一个原则,它将对象或程序的某些部分的控制权转移给容器或框架。我们最常在面向对象编程的上下文中使用它。
小万哥
2023/08/11
5850
深入理解 Spring IoC 和 DI:掌握控制反转和依赖注入的精髓
控制反转和依赖注入模式
一、控制反转和依赖注入两者搭配能像反射工厂那样解决程序集之间的耦合问题,下面将从Asp.Net经典的三层模式多方位的讲解控制反转和依赖注入模式,是如何帮我们进行程序集之间的解耦的。 上图是最基本的三层
郑小超.
2018/01/26
6710
依赖注入和控制反转
用图例来说明一下,先看没有IoC/DI的时候,常规的A类使用C类的示意图,如图所示:
喝茶去
2019/04/16
1K1
依赖注入和控制反转
相关推荐
硬核!如何全面系统地自学Java
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验