大白话理解IoC和DI

ops/2025/1/15 22:00:24/

引言

Spring是Java领域最受欢迎的开发框架之一,其核心功能之一就是Spring容器,也就是IoC容器。这篇文章,我们就来聊聊Spring的两大核心功能,控制反转(IOC)和依赖注入(DI)。

文章思路是这样:

  1. 传统开发存在哪些问题
  2. 为了解决这些问题引入IoC 和DI
  3. 总结

传统开发存在的问题

我最早看到IoCDI这两个名词的时候,我脑子里是懵的,理解不了。既然陌生的东西理解不了,我们就看看它为什么出现?它的出现是为了解决什么问题。

我们先假设有三个类,分别为类A、B和C, 其中,main 函数调用了类A, 类A 依赖了类B和类C,类B依赖类C,如果用传统模式开发,大概是下图中的情况,那么传统的这种开发模式存在什么问题呢?看下图,不难发现对于类C,我们在类A 和类B 中都进行了实例化,也就是new了一个C对象 那么:
第一个问题:代码重复,且浪费内存资源。
第二个问题:耦合性太高了,类C是以硬编码的方式创建出来的,如果类C 发生了重大变化,都会直接影响类A和B,
第三个问题:难以测试。在测试过程中,很难将被测试对象与其依赖的对象解耦,从而无法独立地测试被测试对象的逻辑。
第N个问题:其他的就不多说了
在这里插入图片描述

我们有没有什么方式能解决上面这些问题呢? 有,就是IoC和DI。

接下来我们就分析一下IoCDI是怎样解决这些问题的:

什么是IOC?

IoC( Inversion of Control ) 注意哦, 它是一个技术思想,不是一个技术实现。它描述的是 Java开发领域对象的创建,管理问题,我们看了上面的图就知道在传统开发中,存在依赖时,往往都会new一个依赖的对象,那么在IoC 思想下,就不用去new 对象了。而是由IoC容器去帮我们实例化对象并且管理它。

为什么叫控制反转呢?

1.控制了什么?
控制了对象创建、管理的权力

2.反转了什么?
将控制权交给了外部(IoC容器)

如下图:对于对象的创建和管理都交给了IoC容器,当需要使用的时候,不需要去new了,直接去IoC容器中拿。

在这里插入图片描述

什么是DI

DI:Dependancy Injection (依赖注入)

其实DI和IoC是对同一件事情的不同描述,IoC是一种设计原则,是一种思想,而DI是IoC的一种具体实现,前面我们提到,对象统一交给IoC创建并管理,在依赖的地方不需要去new, 这儿就可以理解依赖注入了:

再看看上面的图,类A 依赖类B和C, 注意看图中的伪代码,类A依赖类B和C,那么只需要在类A中声明要依赖的对象,那么通过构造函数注入或者属性注入或者方法注入的方式,将依赖的对象注入到对象中。

IoC是如何解决难以测试问题的呢?

假设我们有一个简单的应用程序,其中有一个服务类 UserService,它依赖于一个数据访问对象 UserDAO 来获取用户信息。我们想要测试 UserService 中的 getUserById 方法,以确保它能够正确地返回指定用户的信息。

首先,我们来看一下没有使用依赖注入的情况:

public class UserService {private UserDAO userDAO;public UserService() {this.userDAO = new UserDAO(); // 在构造函数中直接创建依赖对象}public User getUserById(int userId) {return userDAO.getUserById(userId);}
}public class UserDAOTest {@Testpublic void testGetUserById() {UserService userService = new UserService(); // 创建被测试对象User user = userService.getUserById(1); // 调用方法// 断言用户信息是否正确assertEquals("husu", user.getName());assertEquals("ricardoyhu@163.com", user.getEmail());}
}

在上面的代码中,UserService 在构造函数中直接创建了 UserDAO 对象,这样在测试的时候就无法替换掉实际的 UserDAO 对象,导致测试无法独立进行,也无法模拟 UserDAO 的行为。

现在,让我们使用依赖注入来改进代码:

public class UserService {@Autowiredprivate UserDAO userDAO;public User getUserById(int userId) {return userDAO.getUserById(userId);}
}public class UserDAOTest {@Testpublic void testGetUserById() {// 创建模拟的UserDAO对象UserDAO mockUserDAO = Mockito.mock(UserDAO.class);// 设置模拟对象的行为when(mockUserDAO.getUserById(1)).thenReturn(new User(1, "husu", "ricardoyhu@163.com"));UserService userService = new UserService(mockUserDAO); // 通过构造函数注入模拟对象User user = userService.getUserById(1); // 调用方法// 断言用户信息是否正确assertEquals("husu", user.getName());assertEquals("ricardoyhu@163.com", user.getEmail());}
}

在上面的代码中,我们@Autowired将 UserDAO 对象注入到了 UserService 中,这样在测试时就可以使用模拟的 UserDAO 对象来替代实际的 UserDAO 对象。我们使用了 Mockito 框架来创建模拟对象,并设置了模拟对象的行为,以模拟 UserDAO 的返回结果。这样一来,我们就可以独立地测试 UserService 中的 getUserById 方法,而不用担心 UserDAO 的实际行为或状态,从而使得测试更加容易进行。

IoC是如何解决代码重复、性能提升的呢?

如何解决代码重复,其实上面已经说到了,就是又IoC容器创建、管理对象,不用到处new了,
说到性能提升就不得不提到IoC容器的生命周期。

IoC容器的生命周期如以下三个阶段

  1. 初始化阶段 : IoC容器在启动时会进行初始化,包括加载配置文件、解析注解、扫描类路径等操作,在这个阶段,IoC容器会创建并管理所有的Bean定义,并根据配置文件或者注解来实例化和装配Bean。
  2. 使用阶段:IoC容器初始化完成后,应用程序可以通过IoC容器来获取所需的Bean对象,并且利用这些对象来完成各种业务逻辑,这个阶段,IoC容器负责管理对象的生命周期,包括对象的创建、依赖注入、初始化等操作。
  3. 销毁阶段:当应用程序关闭时,IoC容器会进行销毁操作,释放资源并销毁所有的Bean对象,在这个阶段,如果Bean类中定义了特定的销毁方法,IOC容器会调用这些方法。如果没有定义销毁方法,IOC容器就不会执行任何额外的销毁操作,而是简单地释放Bean对象所占用的资源,如数据库连接、文件句柄之类的,这个阶段的执行顺序与初始化阶段相关,即先销毁依赖关系较少的Bean,再销毁依赖关系较多的Bean,以保证销毁的顺序正确。

一个Bean 在容器启动时被创建,就会一直存在于容器中,直到应用程序关闭时被销毁,这种管理方式保证了对象的单例性和全局可访问性,也因此提高了系统的性能和效率。

意思就是不会重复创建,不会浪费资源。少了多余的创建和销毁的性能开销,自然就提高系统性能啦。

总结

通过深入理解IOC与DI的核心概念和实践应用,我们可以更好地掌握Spring框架的原理和功能。


http://www.ppmy.cn/ops/25599.html

相关文章

pandas数据分析综合练习50题 - 地区房价分析

数据源 我们将使用一个公开的数据集,“纽约市Airbnb开放数据集”。这个数据集包含了纽约市Airbnb的上万条房源信息,包括价格、位置、房东信息和评论数量等字段。 获取方式1 - 本文资源文件下载 可在文章绑定资源中下载。 获取方式2 - 网页下载 直接…

【深度学习基础(1)】什么是深度学习,深度学习与机器学习的区别、深度学习基本原理,深度学习的进展和未来

文章目录 一. 深度学习概念二. 深度学习与机器学习的区别三. 理解深度学习的工作原理1. 每层的转换进行权重参数化2. 怎么衡量神经网络的质量3. 怎么减小损失值 四. 深度学习已取得的进展五. 人工智能的未来 - 不要太过焦虑跟不上 一. 深度学习概念 先放一张图来理解下人工智能…

基于FPGA的数字信号处理(4)--浮点数的定点化

写在前面 首先要说明的是,题目《浮点数的定点化》中所谓的 浮点数 并不是指 IEEE754 规定的 单精度浮点数 或者 双精度浮点数 等格式,而是指10进制小数。所以说白了,这篇文章要讲的就是如何将10进制小数采用定点数的形式表示。 为什么2进制无…

企业如何通过定制AI智能名片B2B2C商城系统革新营销手段

在日新月异的商业环境中,企业想要立足并蓬勃发展,就必须紧跟时代的步伐,不断革新营销手段。而定制开发AI智能名片B2B2C商城系统正是企业实现这一目标的重要武器。接下来,我们将深入探讨企业如何通过这一系统,在与客户交…

STM32、GD32等驱动AMG8833热成像传感器源码分享

一、AMG8833介绍 1简介 AMG8833是一种红外热像传感器,也被称为热感传感器。它可以用来检测和测量物体的热辐射,并将其转换为数字图像。AMG8833传感器可以感知的热源范围为-20C到100C,并能提供8x8的像素分辨率。它通过I2C接口与微控制器或单…

UE4 Widget制作搜索框

效果: 一、控件层级结构 1.父控件层级结构 2.子控件层级结构 二、蓝图 1.先清除掉创建子项(注意:这里使用的是reverse循环!) 2.判断是否含有关键字,创建子控件

git revert的使用

由于某种原因我们需要撤销掉之前某一次的修改,但是这个修改已经提交,并且后面又经历了好几轮的提交。可能如下这种情况: 那么此时使用git revert再合适不过啦。git revert ${commit_id}就可以将指定commit id的修改撤销,然后提交…

C#编程模式之组合模式(Composite)

创作背景:各位朋友,我们继续C#编程模式的探讨,这次探讨的模式是组合模式。它和桥接模式一样,是一种结构型设计模式,允许使用者将对象组合成树形结构来展示其“部分和整体”的层次结构。要求同样比较严格,用…