Spring 的 IoC 和 DI 详解:从零开始理解与实践
Spring 的 IoC和 DI 详解:从零开始理解与实践
一、IoC(控制反转)
1、什么是 IoC?
IoC 是一种设计思想,它的核心是将对象的创建和管理权从开发者手中转移到外部容器(如 Spring 容器)。通过这种方式,开发者不再需要手动创建对象和管理它们之间的依赖关系,而是将这些任务交给 Spring 容器来完成。
举个例子:
在传统的开发中,如果你需要一个 BookService 来调用 BookDao 的方法,你可能会直接在 BookService 中通过 new BookDao() 创建 BookDao 的实例。这种方式虽然简单,但会导致代码的耦合度很高,难以维护和扩展。
而使用 IoC 后,BookDao 的实例会由 Spring 容器创建,并自动注入到 BookService 中。开发者只需关注业务逻辑,无需关心对象的创建和依赖关系。
2、 IoC 的实现原理
IoC 的实现基于 Spring 容器。Spring 容器负责创建对象、管理对象的生命周期以及对象之间的依赖关系。开发者通过配置文件(如 XML 文件)或注解的方式,将类交给 Spring 容器管理。
3、示例代码
3.1创建类
package dao;public interface BookDao {void save();
}package dao.impl;import dao.BookDao;public class BookDaoImpl implements BookDao {@Overridepublic void save() {System.out.println("BookDao save...");}
}package service;import dao.BookDao;public interface BookService {void save();
}package service.impl;import service.BookService;
import dao.BookDao;public class BookServiceImpl implements BookService {private BookDao bookDao;public void setBookDao(BookDao bookDao) {this.bookDao = bookDao;}@Overridepublic void save() {System.out.println("BookService save...");bookDao.save();}
}
3.2 配置 Spring 容器
<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="bookService" class="service.impl.BookServiceImpl"><property name="bookDao" ref="bookDao"></property></bean><bean id="bookDao" class="dao.impl.BookDaoImpl"></bean>
</beans>
3.3 运行程序
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import service.BookService;public class Main {public static void main(String[] args) {// 创建 Spring 容器ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");// 从容器中获取 BookService 对象BookService bookService = (BookService) applicationContext.getBean("bookService");// 调用 save 方法bookService.save();}
}
4、运行效果
运行程序后,控制台会输出以下内容:
BookService save...
BookDao save...
运行效果讲解:
- Spring 容器会根据
applicationContext.xml文件中的配置,创建BookServiceImpl和BookDaoImpl的实例。 BookDaoImpl的实例由 Spring 容器创建并通过setBookDao方法注入到BookServiceImpl中。
二、DI(依赖注入)
1、什么是 DI?
DI 是 IoC 的一种实现方式,通过外部注入依赖对象,而不是在类内部创建依赖对象。这种方式使得依赖关系更加清晰,便于维护和测试。
2、DI 的实现方式
DI 的实现方式主要有以下几种:
构造函数注入:通过构造函数传递依赖对象。
Setter 注入:通过 Setter 方法注入依赖对象。
字段注入:直接在字段上注入依赖对象(需要使用注解)。
3、示例代码
3.1 使用 Setter 注入
package service.impl;import dao.BookDao;public class BookServiceImpl implements BookService {private BookDao bookDao;// 通过 Setter 方法注入依赖public void setBookDao(BookDao bookDao) {this.bookDao = bookDao;}@Overridepublic void save() {System.out.println("BookService save...");bookDao.save();}
}
3.2 配置 Spring 容器
<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="bookService" class="service.impl.BookServiceImpl"><property name="bookDao" ref="bookDao"></property></bean><bean id="bookDao" class="dao.impl.BookDaoImpl"></bean>
</beans>
3.3 运行程序
运行方式与 IoC 示例相同,代码如下:
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import service.BookService;public class Main {public static void main(String[] args) {// 创建 Spring 容器ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");// 从容器中获取 BookService 对象BookService bookService = (BookService) applicationContext.getBean("bookService");// 调用 save 方法bookService.save();}
}
4 运行效果
运行程序后,控制台会输出以下内容:
BookService save...
BookDao save...
运行效果讲解:
BookDaoImpl的实例由 Spring 容器创建并通过setBookDao方法注入到BookServiceImpl中。- 依赖关系由容器管理,
BookServiceImpl不再直接依赖BookDaoImpl的具体实现。
三、Bean 的类型
1、Bean 的基础类型
1.1 name
可以设置别名,相当于 id 的作用,可以设置多个,后面的 ref 也可以引用 name 中的名字。
<bean id="bookdao" name="dao book se" class="dao.impl.BookDaoImpl"/>
1.2 scope
单例和非单例的设置,具体表现在重新创建时是否会刷新一个新的地址(默认为单例)。
<bean id="bookService" class="service.impl.BookServiceImpl" scope="prototype"/>
2、Bean 的实例化
2.1、构造方法实例化
通过反射的原理(直接通过class类进行)直接进行访问构造函数,无论是公共还是私有都能强制进行访问,不能设置实参,会报错
注:没有任何改变,系统自带就有,但不能添加一个有实参的构造,这样系统不会自动生成会报错
2.2、静态方法实例化
factory是一个中转站,通过改变xml中的获取方式类获取到factory中的new方法,本质还是在获取new中的对象
public static bookdao getOrderDao(){return new bookdaoimpl();
}
方式二:静态方法实例化<bean id="bookfactory" class="factory.factory1" factory-method="getOrderDao"></bean>
factory-method用于获取类中的这个方法
2.3、动态工厂

方法三:动态方法实例化
<bean id="bookfactory" class="factory.factory2"></bean>
<bean id="dao" factory-method="getOrderDao" factory-bean="bookfactory"></bean>
第一步先实例化对象,也就是第二行代码
第二部调用实例化的对象, factory-bean获取第一部的id名
2.4、factorybean
通过接口来实例化一些方法,减少xml中的操作
public class factory3 implements FactoryBean<bookdao>{@Overridepublic boolean isSingleton() {return true;}@Overridepublic bookdao getObject() throws Exception {return new bookdaoimpl();}@Overridepublic Class<?> getObjectType() {return bookdao.class;}
}
isSingleton()设置是否单例
getObject() 获取返回对象
Type设置继承类型
方法四:factoryBean实例化
<bean id="factoryBean" class="factory.factory3"></bean>
3、Bean 的生命周期(两种方法)
3.1、直接创建
public void service(){System.out.println("book dao save.......");
}
public void init(){System.out.println("init");
}
public void destory(){System.out.println("destory");
}
需要在xml中加参数
<bean id="bookdao" name="dao book se" class="dao.impl.bookdaoimpl" init-method="init" destroy-method="destory"/>
init-method="init"设置初始化
destroy-method="destory"设置销毁
销毁的执行需要程序中进行close关闭后才能运行
ClassPathXmlApplicationContext cts = new ClassPathXmlApplicationContext("application.xml");
bookdao bookdao = (bookdao)cts.getBean("bookdao");
bookdao.service();
cts.close();
更改了ApplicationContext为ClassPathXmlApplicationContext
增加了cts.close();
3.2、接口创建实现
public class bookdaoimpl implements dao.bookdao, InitializingBean, DisposableBean {public void service(){System.out.println("book dao save.......");}@Overridepublic void destroy() throws Exception {System.out.println("destory.........");}@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("init........");}
}
实现了
InitializingBean, DisposableBean 两个接口
四、注入类型
1、Setter 注入
1.1 引用类型
通过 Setter 方法注入引用类型
public class BookServiceImpl {private BookDao bookDao;public void setBookDao(BookDao bookDao) {this.bookDao = bookDao;}
}
<bean id="bookService" class="service.impl.BookServiceImpl"><property name="bookDao" ref="bookDao"></property>
</bean>
ref用于设置这里的名字,name跟类中的名字相同
1.2 简单类型
通过 Setter 方法注入简单类型。
public class BookDaoImpl {private String databaseName;private int connectionNum;public void setDatabaseName(String databaseName) {this.databaseName = databaseName;}public void setConnectionNum(int connectionNum) {this.connectionNum = connectionNum;}
}
<bean id="bookDao" class="dao.impl.BookDaoImpl"><property name="databaseName" value="mysql"></property><property name="connectionNum" value="666"></property>
</bean>
value设置里面的值,没有顺序之分
2、构造器注入
2.1 引用类型
通过构造函数注入引用类型。
public class BookServiceImpl {private BookDao bookDao;public BookServiceImpl(BookDao bookDao) {this.bookDao = bookDao;}
}
<bean id="bookService" class="service.impl.BookServiceImpl"><constructor-arg name="bookDao" ref="bookDao"></constructor-arg>
</bean>
2.2 简单类型
通过构造函数注入简单类型。
public class BookDaoImpl {private String databaseName;private int connectionNum;public BookDaoImpl(String databaseName, int connectionNum) {this.databaseName = databaseName;this.connectionNum = connectionNum;}
}
<bean id="bookDao" class="dao.impl.BookDaoImpl"><constructor-arg name="databaseName" value="mysql"></constructor-arg><constructor-arg name="connectionNum" value="666"></constructor-arg>
</bean>
2.3命名的其他操作
name的命名可能耦合度过高
1、采取type=“” 类型来进行确定
2、采取index=""位置来进行确定

3、自动注入
在xml中进行配置直接进行注入
<bean class="com.itheima.dao.impl.BookDaoImpl"/>
<!--autowire属性:开启自动装配,通常使用按类型装配-->
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl" autowire="byType"/>
autowire一般情况使用bytype按类型
当有两个名字的情况进行按名字,名字取决于dao中的private类型
注意:setter类型不能够忘记,不然会报错

4、集合注入
通过 XML 配置注入集合类型。
public class BookDaoImpl {private int[] array;private List<String> list;private Set<String> set;private Map<String, String> map;private Properties properties;// Getter 和 Setter 方法
}
<bean id="bookDao" class="dao.impl.BookDaoImpl"><property name="array"><array><value>100</value><value>200</value><value>300</value></array></property><property name="list"><list><value>itcast</value><value>itheima</value></list></property><property name="set"><set><value>itcast</value><value>itheima</value></set></property><property name="map"><map><entry key="country" value="china"/><entry key="province" value="henan"/></map></property><property name="properties"><props><prop key="country">china</prop><prop key="province">henan</prop></props></property>
</bean>
五、总结
IoC 和 DI 是 Spring 框架的核心概念,它们的主要作用是降低代码的耦合度,提高代码的灵活性和可维护性。
- IoC:通过将对象的创建和管理交给 Spring 容器,开发者只需关注业务逻辑。
- DI:通过依赖注入的方式,使得依赖关系更加清晰,便于维护和测试。
在实际开发中,合理使用 IoC 和 DI 能够显著提高代码的质量和可维护性。希望这篇博客能够帮助你更好地理解和应用 Spring 的 IoC 和 DI!
相关文章:
Spring 的 IoC 和 DI 详解:从零开始理解与实践
Spring 的 IoC和 DI 详解:从零开始理解与实践 一、IoC(控制反转) 1、什么是 IoC? IoC 是一种设计思想,它的核心是将对象的创建和管理权从开发者手中转移到外部容器(如 Spring 容器)。通过这种…...
Python Cookbook-5.12 检查序列的成员
任务 你需要对一个列表执行很频繁的成员资格检査。而in操作符的 O(n)时间复杂度对性能的影响很大,你也不能将序列转化为一个字典或者集合,因为你还需要保留原序列的元素顺序。 解决方案 假设需要给列表添加一个在该列表中不存在的元素。一个可行的方法…...
签名过期怎么办?
1无论是证书到期还是被封停,只需要找到签名服务商,重新签名就可以了,但签名经常性过期会造成app用户流失,所以我们在选择签名时需要注意,在资金充足的情况下,优先选择独立、稳定签名,接下来我们…...
ZYNQ笔记(四):AXI GPIO
版本:Vivado2020.2(Vitis) 任务:使用 AXI GPIO IP 核实现按键 KEY 控制 LED 亮灭(两个都在PL端) 一、介绍 AXI GPIO (Advanced eXtensible Interface General Purpose Input/Output) 是 Xilinx 提供的一个可…...
实操(环境变量)Linux
环境变量概念 我们用语言写的文件编好后变成了程序,./ 运行的时候他就会变成一个进程被操作系统调度并运行,运行完毕进程相关资源被释放,因为它是一个bash的子进程,所以它退出之后进入僵尸状态,bash回收他的退出结果&…...
【补题】P9423 [蓝桥杯 2023 国 B] 数三角
题意:小明在二维坐标系中放置了 n 个点,他想在其中选出一个包含三个点的子集,这三个点能组成三角形。然而这样的方案太多了,他决定只选择那些可以组成等腰三角形的方案。请帮他计算出一共有多少种选法可以组成等腰三角形ÿ…...
Word / WPS 页面顶部标题 段前间距 失效 / 不起作用 / 不显示,标题紧贴页眉 问题及解决
问题描述: 在 Word 或者 WPS 里面,如果不是新的一节,而是位于新的一页首行时,不管怎么设置段前间距,始终是失效的,实际段前间距一直是零。 解决方案: 查询了很多方案均无法解决问题ÿ…...
Mysql自动增长数据的操作(修改增长最大值)
在MySQL中,如果你想要修改一个表的自增长(auto-increment)属性的起始值,可以使用ALTER TABLE语句。这对于初始化新环境或修复损坏的自增长计数器特别有用。下面是如何操作的一些步骤: 查看当前自增长值 首先ÿ…...
Linux自行实现的一个Shell(15)
文章目录 前言一、头文件和全局变量头文件全局变量 二、辅助函数获取用户名获取主机名获取当前工作目录获取最后一级目录名生成命令行提示符打印命令行提示符 三、命令处理获取用户输入解析命令行执行外部命令 四、内建命令添加环境变量检查和执行内建命令 五、初始化初始化环境…...
在 Q3D 中提取汇流条电感
汇流条排简介和设计注意事项 汇流条排是用于配电的金属导体,在许多应用中与传统布线相比具有设计优势。在设计母线排时,必须考虑几个重要的因素: 低电感:高频开关内容会导致无功损耗,从而降低效率电容:管…...
MySQL:事务的理解
一、CURD不加控制,会有什么问题 (1)因为,MySQL里面存的是数据,所以很有可能会被多个客户访问,所以mysqld可能一次会接受到多个关于CURD的请求。(2)且mysql内部是采用多线程来完成数…...
[raspberrypi 0w and respeaker 2mic]实时音频波形
0. 环境 ubuntu22主机, 192.168.8.162, raspberry 0w, 192.168.8.220 路由器 1. 树莓派 # rpi - send.py # 或者命令行:arecord -D plughw:1,0 -t wav -f cd -r 16000 -c 2 | nc 192.168.8.162 12345import socket imp…...
python 基础:句子缩写
n int(input()) for _ in range(n):words input().split()result ""for word in words:result word[0].upper()print(result)知识点讲解 input()函数 用于从标准输入(通常是键盘)读取用户输入的内容。它返回的是字符串类型。例如在代码中…...
Ruoyi-vue plus 5.2.2 flowble 结束节点异常错误
因业务要求, 我在结束节点的结束事件中,制作了一个归档的事件,来执行一个业务。 始终都会报错, 错误信息 ${archivTemplateListener} did not resolve to an implementation of interface org.flowable.engine.delegate.Execution…...
Sublime Text使用教程(用Sublime Text编写C语言程序)
Sublime Text 是一款当下非常流行的文本编辑器,其功能强大(提供有众多的插件)、界面简洁、还支持跨平台使用(包括 Mac OS X、Linux 和 Windows)。 在程序员眼中,Sublime Text 不仅仅是一个文本编辑器&…...
【1】k8s集群管理系列--包应用管理器之helm
一、helm概述 Helm核心是模板,即模板化K8s YAML文件。 通过模板实现Chart高效复用,当部署多个应用时,可以将差异化的字段进行模板化,在部署时使用-f或 者–set动态覆盖默认值,从而适配多个应用 helm工作流程…...
【书籍】DeepSeek谈《持续交付2.0》
目录 一、深入理解1. 核心理念升级:从"自动化"到"双环模型"2. 数字化转型的五大核心能力3. 关键实践与案例4. 组织与文化变革5. 与其它框架的关系6. 实际应用建议 二、对于开发实习生的帮助1. 立刻提升你的代码交付质量(技术验证环实…...
Mysql表的操作(2)
1.去重 select distinct 列名 from 表名 2.查询时排序 select 列名 from 表名 order by 列名 asc/desc; 不影响数据库里面的数据 错误样例 : 但结果却有点出乎意料了~为什么会失败呢? 其实这是因为书写的形式不对,如果带了引号,…...
智能物联网网关策略部署
实训背景 某智慧工厂需部署物联网网关,实现以下工业级安全管控需求: 设备准入控制:仅允许注册MAC地址的传感器接入(白名单:AA:BB:CC:DD:EE:FF)。协议合规性:禁止非Modbus TCP(端口…...
神经网络语言模型与统计语言模型的比较
神经网络语言模型(Neural Language Models, NLMs)与统计语言模型(Statistical Language Models, SLMs)是自然语言处理(NLP)中两类核心的语言建模方法,其核心差异体现在建模方式、表示能力、数据…...
Java学习总结-线程池
线程池是什么? 线程池就是一个可以复用线程的技术。 假若不用线程池的问题:创建新线程开销很大,不能来一个任务就就创建一个新线程。 如何创建线程池对象? 方法一:使用ExecutorService的实现类ThreadPoolExecutor创…...
Android 中绕过hwbinder 实现跨模块对audio 的HAL调用
需求 Audio 模块中专门为 TV 产品添加了一些代码,需要在 hdmi 的 HAL 代码中进行调用以完成某些功能。 解决方法 首先将 hdmi HAL 要调用的 audio 接口函数所在的 .so 链接到最基本的 lib.primay.amlogic.so 中(其它平台上这个 .so 文件的名字也可能是…...
【DB2】事务日志满/归档占用较大问题处理记录
某DB2环境经常报错The active log is full and is held by...,并且归档磁盘占用较大 事务日志满 事务日志满可以理解为Oracle的redo追尾,即业务写入量大于redo刷盘速度,这时候其他SQL会陷入等待,容易造成性能问题 一般由两方面原…...
基于CNN-BiLSTM-GRU的深度Q网络(Deep Q-Network,DQN)求解移动机器人路径规划,MATLAB代码
一、深度Q网络(Deep Q-Network,DQN)介绍 1、背景与动机 深度Q网络(DQN)是深度强化学习领域的里程碑算法,由DeepMind于2013年提出。它首次在 Atari 2600 游戏上实现了超越人类的表现,解决了传统…...
CVE-2025-29927 Next.js 中间件鉴权绕过漏洞
Next.js Next.js 是一个基于 React 的现代 Web 开发框架,用来构建高性能、可扩展的 Web 应用和网站。 CVE-2025-29927 Next.js 中间件鉴权绕过漏洞 CVE-2025-29927是Next.js框架中的一个授权绕过漏洞,允许攻击者通过特制的HTTP请求绕过在中间件中执行…...
数据结构(五)——AVL树(平衡二叉搜索树)
目录 前言 AVL树概念 AVL树的定义 AVL树的插入 右旋转 左旋转 左右双旋 右左双旋 插入代码如下所示 AVL树的查找 AVL树的遍历 AVL树的节点个数以及高度 判断平衡 AVL树代码如下所示 小结 前言 前面我们在数据结构中介绍了二叉搜索树,其中提到了二叉搜…...
C++类型转换详解
目录 一、内置 转 内置 二、内置 转 自定义 三、自定义 转 内置 四、自定义 转 自定义 五、类型转换规范化 1.static_case 2.reinterpret_cast 3.const_cast 4.dynamic_cast 六、RTTI 一、内置 转 内置 C兼容C语言,在内置类型之间转换规则和C语言一样的&am…...
【前端】【React】性能优化三件套useCallback,useMemo,React.memo
一、总览:性能优化三件套 useCallback(fn, deps):缓存函数,避免每次渲染都新建函数。useMemo(fn, deps):缓存值(计算结果),避免重复执行计算。React.memo(Component):缓存组件的渲染…...
excel数据透视表大纲格式改为表格格式
现有这样一个数据透视表: 想要把他变成这样的表格格式: 操作步骤: 第一步: 效果: 第二步: 效果: 去掉分类汇总: 效果: 去掉展开/折叠按钮: 操作方式…...
pycharm中安装Charm-Crypto
一、安装依赖 1、安装gcc、make、perl sudo apt-get install gcc sudo apt-get install make sudo apt-get install perl #检查版本 gcc -v make -v perl -v 2、安装依赖库m4、flex、bison(如果前面安装过pypbc的话,应该已经装过这些包了) sudo apt-get update sudo apt…...
