深入理解 Spring 框架中的 IOC 容器
一、Spring 框架概述
Spring 框架是一个轻量级的 Java 开发框架,由 Rod Johnson 在 2003 年创建。它的诞生旨在简化企业级应用开发的复杂性。Spring 框架提供了诸如 IoC(控制反转)和 AOP(面向切面编程)等核心功能,并且拥有众多的模块,能够灵活应对不同的开发场景,包括 Web 开发、数据访问、消息处理等多个方面。在企业级 Java 开发领域,Spring 框架已经成为了不可或缺的基础框架之一。
二、控制反转(IoC)与依赖注入(DI)
(一)IoC 概念
IoC,即控制反转,其核心思想是将对象的创建和依赖关系的管理交给 Spring 容器。在传统的 Java 开发中,对象之间的依赖关系通常由对象自身来管理,这就导致了组件之间的耦合度较高。而在 Spring 框架中,通过 IoC 机制,开发者无需再过多关注对象的创建和管理细节,能够更专注于业务逻辑的实现。例如,在一个复杂的企业级应用中,各个业务组件之间存在着错综复杂的依赖关系,如果每个组件都自行创建和管理所依赖的对象,那么一旦某个对象的创建逻辑发生变化,就可能需要在多个地方进行修改,维护成本极高。而借助 Spring 的 IoC 容器,所有对象的创建和依赖关系都由容器统一管理,大大降低了组件之间的耦合度。
(二)DI 概念及其优势
DI,即依赖注入,是 IoC 的一种实现方式。它使得代码的可测试性大大增强。在进行单元测试时,通过依赖注入,我们可以方便地为对象注入模拟依赖。比如,在测试一个服务类时,该服务类通常依赖于数据库访问对象来进行数据操作。在传统的测试方式下,我们需要搭建完整的数据库环境来测试服务类的功能,这不仅复杂而且耗时。而利用依赖注入,我们可以为服务类注入一个模拟的数据库访问对象,这个模拟对象可以按照我们的预期返回测试数据,从而更高效地对服务类代码进行测试,无需依赖真实的数据库环境。
三、IOC 解决的核心问题
在 Java 编程中,对象之间的依赖关系如果处理不当,会导致程序耦合性过高。例如,当 A 类需要使用 B 类的方法时,通常需要在 A 类中创建 B 类的对象。如果存在多个类之间相互依赖,如 A 类依赖 B 类,B 类依赖 C 类,C 类又依赖 A 类,形成循环依赖,一旦其中一个类出现问题,整个系统的稳定性都会受到影响。Spring 的 IoC 将对象的创建权力反转给了 IOC 容器,在容器中统一创建和管理各个对象,其他类只需要从容器中获取所需对象即可,极大地降低了程序的耦合性。
四、IOC 容器的底层原理
IOC 的实现依赖于以下三门重要技术:
(一)dom4j 解析 xml 文档
在早期的 Spring 配置中,大量使用 XML 文件来定义 Bean 的配置信息,包括 Bean 的名称、类名、属性值以及依赖关系等。dom4j 是一个功能强大的 XML 解析工具,它提供了简洁易用的 API,能够方便地读取和解析 XML 文件。Spring 利用 dom4j 读取配置文件中的信息,将其转化为容器内部可识别的数据结构,以便后续根据这些配置信息创建和管理对象。例如,通过 dom4j 解析 XML 文件中关于某个 Bean 的配置,获取其类名等关键信息,为后续创建该 Bean 实例做准备。
(二)工厂模式
工厂模式是一种创建对象的设计模式。在 Spring 的 IOC 容器中,它就像是一个大型的对象工厂。当容器接收到创建某个对象的请求时,它会根据配置信息,通过类似工厂的方式来创建对象。容器根据配置文件中指定的类名,使用反射机制来实例化对象,并根据配置的属性信息对对象进行初始化。这种方式将对象的创建逻辑封装在容器内部,外部调用者只需要向容器请求对象,而无需关心对象具体的创建过程,实现了对象创建和使用的分离。
(三)采用反射设计模式创建对象
反射是 Java 的一项强大特性,它允许程序在运行时动态地获取类的信息,并创建对象、调用方法等。在 Spring 的 IOC 容器中,反射机制起着至关重要的作用。当容器通过 dom4j 解析配置文件获取到 Bean 的类名后,利用反射机制,根据类名加载对应的类,并通过反射调用类的构造函数来创建对象实例。同时,对于对象的属性设置,也可以通过反射获取类的属性信息,并进行赋值操作。例如,当配置文件中指定了某个 Bean 的属性值时,容器通过反射找到对应的属性,并将配置的值注入到对象中。
五、IOC 容器的具体实现方式
(一)BeanFactory
BeanFactory 是 Spring 内部使用的 IOC 容器接口,它在加载配置文件时并不会立即创建对象,而是在实际使用对象时才进行创建。这种延迟加载的方式在某些场景下可以提高系统的启动性能,尤其是对于资源消耗较大的对象。不过,由于它的功能相对较为基础,一般不直接提供给开发人员使用。
(二)ApplicationContext
ApplicationContext 是 BeanFactory 接口的子接口,它提供了更为丰富和强大的功能,通常由开发人员在实际项目中使用。与 BeanFactory 不同,ApplicationContext 在加载配置文件时会立即创建所有配置的对象。这意味着在系统启动阶段,所有 Bean 就已经被创建并初始化完毕,后续使用时可以直接从容器中获取,响应速度更快。此外,ApplicationContext 还提供了诸如国际化支持、资源加载、事件发布等高级功能,使得应用开发更加便捷。
六、Spring 框架的 Bean 管理
(一)Bean 管理概述
Bean 管理主要涵盖两个关键操作:对象的创建和属性的注入。在 Spring 框架中,通过合理管理 Bean,能够实现对象的高效创建、配置和使用,充分发挥 IOC 容器的优势。
(二)Bean 管理操作的两种方式
- 基于 xml 配置文件的方式
- 创建对象:在 xml 配置文件中,通过
<bean>标签来配置要创建的对象。例如<bean id="demo" class="com.qcby.service.Demo" />,其中id属性指定了对象在容器中的唯一标识,class属性指定了对象所属的类。在创建对象时,默认会执行无参构造方法来完成对象的创建。 - 注入属性:
- set 方法注入值:在类中编写属性,并为其提供对应的 set 方法。然后在配置文件中使用
<property>标签来完成属性值的注入。例如:
- set 方法注入值:在类中编写属性,并为其提供对应的 set 方法。然后在配置文件中使用
- 创建对象:在 xml 配置文件中,通过
<bean id="user" class="com.qcby.service.User"><property name="age" value="18"></property><property name="name" value="张三"></property><property name="demo" ref="demo"></property>
</bean>
<bean id="demo" class="com.qcby.service.Demo" />
这里name属性指定类中的属性名称,value用于注入普通类型的值,ref用于引用其他 Bean 对象。
- 数组、集合 (List,Set,Map) 等的 set 注入:对于包含数组、集合等复杂类型属性的注入,需要在
<property>标签内嵌套相应的标签来设置值。例如:
<bean id="collectionBean" class="com.qcby.service.CollectionBean"><property name="strs"><array><value>美美</value><value>小凤</value></array></property><property name="list"><list><value>熊大</value><value>熊二</value></list></property><property name="map"><map><entry key="aaa" value="老王"/><entry key="bbb" value="小王"/></map></property>
</bean>
- 属性构造方法方式注入值:对于通过构造函数来初始化成员变量的情况,可以在配置文件中使用
<constructor-arg>标签来注入值。例如:
<bean id="car" class="com.qcby.service.Car"><constructor-arg name="cname" value="奔驰"></constructor-arg><constructor-arg name="money" value="35"></constructor-arg>
</bean>
- 数组、集合 (List,Set,Map) 等的构造器注入:与构造方法注入类似,只是在
<constructor-arg>标签内嵌套相应的集合标签来设置值。例如:
<bean id="user" class="com.qcby.service.UserService"><constructor-arg index="0"><array><value>aaa</value><value>bbb</value><value>ccc</value></array></constructor-arg><constructor-arg index="1"><list><value>小黑</value><value>小白</value></list></constructor-arg><constructor-arg index="2"><map><entry key="aaa" value="小黑"/><entry key="bbb" value="小号"/></map></constructor-arg>
</bean>
- 基于注解的方式
- 什么是注解:注解是代码中的特殊标记,格式为
@注解名称(属性名称=属性值,属性名称=属性值...)。它可以作用在类、方法、属性等上面,目的是简化 XML 配置。 - Spring 针对 Bean 管理中创建对象提供的注解:
@Component:用于标记普通的类,将其纳入 IOC 容器管理。@Controller:主要用于表现层的类,同样将类纳入 IOC 容器管理。@Service:用于业务层的类,实现类的容器管理。@Repository:针对持久层的类,使其受 IOC 容器管理。这四个注解功能类似,都可以用来创建 bean 实例。如果不指定名称,默认使用类名,首字母小写。例如:
- 什么是注解:注解是代码中的特殊标记,格式为
@Controller(value="us")
public class UserServiceImpl implements UserService {public void hello() {System.out.println("使用注解,方便吧!");}
}
- 用注解的方式创建对象:首先编写接口和实现类,然后在需要管理的类上添加相应注解,并在配置文件中开启注解扫描。例如:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd"><!--开启注解扫描 com.qcby所有的包中的所有的类--><context:component-scan base-package="com.qcby"/>
</beans>
- 用注解的方式实现属性注入:
@Value:用于注入普通类型(如 String,int,double 等)的值。例如:
@Component(value = "c")
public class Car {@Value("大奔2")private String cname;@Value(value = "400000")private Double money;
}
@Autowired:默认按类型进行自动装配(用于引用类型)。例如:
@Component(value = "c")
public class Car {@Autowiredprivate Person person;
}
@Qualifier:不能单独使用,必须和@Autowired一起使用,强制使用名称注入。例如:
@Component(value = "c")
public class Car {@Autowired@Qualifier(value = "person")private Person person;
}
@Resource:Java 提供的注解,也被 Spring 支持。使用name属性按名称注入。例如:
@Component(value = "c")
public class Car {@Resource(name = "person")private Person person;
}
(三)IOC 纯注解的方式
在微服务架构开发中,纯注解方式愈发重要,因为它旨在替换掉所有的配置文件,进一步简化开发流程。不过,使用纯注解需要编写配置类。
- 常用的注解总结
@Configuration:声明该类是一个配置类,用于替代传统的 XML 配置文件。@ComponentScan:用于扫描指定的包结构,将包内符合条件的类纳入 IOC 容器管理。
- 具体实现示例
- 编写实体类:
@Component
public class Order {@Value("北京")private String address;@Overridepublic String toString() {return "Order{" +"address='" + address + '\'' +'}';}
}
- 编写配置类,替换掉 applicationContext.xml 配置文件:
@Configuration
// 扫描指定的包结构
@ComponentScan(value = "com.qcby")
public class SpringConfig {
}
- 测试方法的编写:
package com.qcby.test;
import com.qcby.demo4.Order;
import com.qcby.demo4.SpringConfig;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class Demo4 {@Testpublic void run(){// 创建工厂,加载配置类ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfig.class);// 获取到对象Order order = (Order) ac.getBean("order");System.out.println(order);}
}
七、总结
Spring 框架中的 IOC 容器通过控制反转和依赖注入的理念,结合 dom4j 解析、工厂模式和反射机制等底层技术,为 Java 开发者提供了一种高效、灵活的对象管理和依赖处理方式。无论是基于 xml 配置文件的传统方式,还是基于注解甚至纯注解的现代方式,IOC 容器都能帮助我们轻松解决程序耦合性高的问题,提升代码的可维护性、可测试性和可扩展性。在实际的项目开发中,深入理解和熟练运用 IOC 容器,将为构建高质量的 Java 应用奠定坚实的基础。希望本文能够帮助读者全面掌握 Spring 框架中 IOC 容器的精髓,并在开发实践中发挥其最大价值。
相关文章:
深入理解 Spring 框架中的 IOC 容器
一、Spring 框架概述 Spring 框架是一个轻量级的 Java 开发框架,由 Rod Johnson 在 2003 年创建。它的诞生旨在简化企业级应用开发的复杂性。Spring 框架提供了诸如 IoC(控制反转)和 AOP(面向切面编程)等核心功能&…...
深入理解 Java 中 instanceof 操作符
目录 1. instanceof 的基本用法 1.1 语法 1.2 示例 2. instanceof 的用途 2.1 类型检查 2.2 类型转换 2.3 多态编程 3. instanceof 的注意事项 3.1 null 检查 3.2 接口检查 3.3 继承关系 3.4 性能问题 4. instanceof 代码示例 4.1 多态处理 4.2 接口检查 4.3 n…...
2025前端面试题记录
vue项目目录的执行顺序是怎么样的? 1、package.json 在执行npm run dev时,会在当前目录寻找package.json文件,此文件包含了项目的名称版本、项目依赖等相关信息。 2、webpack.config.js(会被vue-cli脚手架隐藏) 3、vue.config.js 对…...
复变函数摘记2
复变函数摘记2 3. 级数3.1 复数项级数3.2 复变幂级数3.3 泰勒级数3.4 洛朗级数 3. 级数 \quad 复数项级数的一般项 α n a n i b n \alpha_na_n\text{i}b_n αnanibn 为复数,与高等数学中无穷级数的分析方式类似,也是通过和函数来研究级数的收敛…...
光纤的频率和带宽
光纤通信中的频率和带宽涉及光波的物理特性以及通信系统的设计,以下是详细解释: 1. 光纤的工作频率 光纤通信利用光波作为载波,工作频率主要在近红外波段,具体频段和对应的波长如下: C波段(Conve…...
高频面试题(含笔试高频算法整理)基本总结回顾67
干货分享,感谢您的阅读! (暂存篇---后续会删除,完整版和持续更新见高频面试题基本总结回顾(含笔试高频算法整理)) 备注:引用请标注出处,同时存在的问题请在相关博客留言…...
Kafka--常见问题
1.为什么要使用 Kafka,起到什么作用 Kafka是一个高吞吐量、分布式、基于发布订阅的消息系统,它主要用于处理实时数据流 Kafka 设计上支持高吞吐量的消息传输,每秒可以处理数百万条消息。它能够在处理大量并发请求时,保持低延迟和…...
优选算法的睿智之林:前缀和专题(一)
专栏:算法的魔法世界 个人主页:手握风云 目录 一、前缀和 二、例题讲解 2.1. 一维前缀和 2.2. 二维前缀和 2.3. 寻找数组的中心下标 2.4. 除自身以外数组的乘积 一、前缀和 前缀和算法是一种用于处理数组或序列数据的算法,其核心思想是…...
嵌入式八股文学习——STL相关内容学习
文章目录 map和set的区别与实现1. map和set的区别2. 为什么set的元素和map的key不可修改? map和set的实现1. map的实现原理map的操作:map的特点: 2. set的实现原理set的操作:set的特点: map和set的底层原理(…...
【清华大学】AIGC发展研究(3.0版)
目录 AIGC发展研究报告核心内容一、团队简介二、AI哲学三、国内外大模型四、生成式内容(一)文本生成(二)图像生成(三)音乐生成(四)视频生成 五、各行业应用六、未来展望 AIGC发展研究…...
JavaSE1.0(基础语法之运算符)
算术运算符 基础运算之加 减 乘 除 取余( - * / %) 运算符之相加( ) public static void main(String[] args) {System.out.println("Hello world!");int a 10;int b 20;int c a b;System.out.println(c);//…...
二十五、实战开发 uni-app x 项目(仿京东)- 前后端轮播图
定义了一个名为 Swiper 的Java类,用于表示一个轮播图实体。它使用了 Jakarta Persistence API (JPA) 来映射数据库表,并使用了 Lombok 库来简化代码。以下是对代码的详细讲解: 1. 包声明 package com.jd.jdmall.model; 这行代码声明了该类所在的包路径为 com.jd.jdmall.mode…...
ubuntu设置开机自动运行应用
系统版本:Ubuntu 24.04.1 LTS桌面版 按招网上的资料显示,当前版本主要的实现方式有以下两种, 方式1:通过图形界面的【启动应用程序】设置开机自启动;方式2:配置为服务实现开机自启动。 但是在我的电脑上方…...
蓝桥与力扣刷题(蓝桥 数的分解)
题目:把 2019分解成 3个各不相同的正整数之和,并且要求每个正整数都不包含数字 2 和 4,一共有多少种不同的分解方法? 注意交换 3 个整数的顺序被视为同一种方法,例如 1000100118和 1001100018 被视为同一种。 解题思…...
用ACM模式模板刷hot100
面试手撕给的模板基础上写 给的模板一般是下面这样 把while内容删除(一般刷hot100题目输入不需要同时输入几组) 第一个方法里写处理输入输出 自己再写一个方法,就是力扣里的核心代码(加上static) 第一个处理输入输…...
Java IO 流:从字节到字符再到Java 装饰者模式(Decorator Pattern),解析与应用掌握数据流动的艺术
在 Java 编程中,IO(输入输出)流是处理数据输入输出的核心工具。无论是读取文件、网络通信,还是处理用户输入,IO 流都扮演着重要角色。本文将深入探讨 Java IO 流的核心概念、分类、经典代码实例及其应用场景࿰…...
爬虫案例-爬取某站视频
文章目录 1、下载FFmpeg2、爬取代码3、效果图 1、下载FFmpeg FFmpeg是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。 点击下载: ffmpeg 安装并配置 FFmpeg 步骤: 1.下载 FFmpeg: 2.访问 FFmpeg 官网。 3.选择 Wi…...
nacos-未经授权创建用户漏洞
1、修改配置文件 vim application.properties# 修改配置项 nacos.core.auth.enabledtrue nacos.core.auth.enable.userAgentAuthWhitefalse2、重启nacos systemctl restart nacos3、验证 打开nacos部署服务器输入命令 curl -XPOST -d “usernametest123&passwordtest!123…...
C++:IO库
一、C IO库的架构 C标准库中的IO系统基于流(Stream)的概念,分为三层结构: 流对象(如cin, cout, fstream)流缓冲区(streambuf,负责底层数据处理)数据源/目的…...
企业级前端架构设计与实战
一、架构设计核心原则 1.1 模块化分层架构 典型目录结构: src/├── assets/ # 静态资源├── components/ # 通用组件├── pages/ # 页面模块├── services/ # API服务层├── store/ # 全局状态管理├── uti…...
从入门到精通【MySQL】 CRUD
文章目录 📕1. Create 新增✏️1.1 单行数据全列插入✏️1.2 单行数据指定列插入✏️1.3 多行数据指定列插入 📕2. Retrieve 检索✏️2.1 全列查询✏️2.2 指定列查询✏️2.3 查询字段为表达式✏️2.4 为查询结果指定别名✏️2.5 结果去重查询 …...
08_双向循环神经网络
双向网络 概念 双向循环神经网络(Bidirectional Recurrent Neural Network, BiRNN)通过同时捕捉序列的正向和反向依赖关系,增强模型对上下文的理解能力。与传统的单向网络不同,BIRNN 能够同时从过去和未来的上下文信息中学习,从而提升模型的…...
JSON数据修改的实现
JSON数据的修改 示例代码如下: using System.Collections; using System.Collections.Generic; using UnityEngine; //C#命名空间(以System开头) using System.IO; using LitJson; public class JsonChange : MonoBehaviour {// Start is called befor…...
2025年Postman的五大替代工具
虽然Postman是一个广泛使用的API测试工具,但许多用户在使用过程中会遇到各种限制和不便。因此,可能需要探索替代解决方案。本文介绍了10款强大的替代工具,它们能够有效替代Postman,成为你API测试工具箱的一部分。 什么是Postman&…...
(四)---四元数的基础知识-(定义)-(乘法)-(逆)-(退化到二维复平面)-(四元数乘法的导数)
使用四元数的原因 最重要的原因是因为传感器的角速度计得到的是三个轴的角速度, 这三个轴的角速度合成一个角速度矢量, 结果就是在微小时间内绕着这个角速度矢量方向为轴旋转一定角度. 截图来源网址四元数 | Crazepony开源四轴飞行器...
汇能感知高品质的多光谱相机VSC02UA
VSC02UA概要 VSC02UA是一款高品质的200万像素的光谱相机,适用于工业检测、农业、医疗等领域。VSC02UA 包含 1600 行1200 列有源像素阵列、片上 10 位 ADC 和图像信号处理器。它带有 USB2.0 接口,配合专门的电脑上位机软件使用,可进行图像采集…...
【SpringBoot】MorningBox小程序的完整后端接口文档
以下是「晨光宅配」小程序的完整接口文档,涵盖了所有12个表的接口。 每个接口包括请求方法、URL、请求参数、响应格式和示例 接口文档 1. 用户模块 1.1 获取用户信息 URL: /user/{userId}方法: GET请求参数: userId (路径参数): 用户ID响应格式:{"userId": 1,&qu…...
Blazor+PWA技术打造全平台音乐播放器-从音频缓存到离线播放的实践之路
开局三张图… 0.起源 主要是自己现在用的是苹果手机,虽然手机很高级,但是想听自己喜欢的歌曲确是不容易,在线app都要付费,免费的本地播放器都不太好用(收费的也不太行),基础功能都不满足。此外…...
使用LangChain开发智能问答系统
代码地址见文末 1. 项目配置 1.1 Neo4j 数据库配置 1. 安装与环境变量 解压路径:将neo4j-community-5.x.x.zip解压至D:\neo4j-community-5.x.x环境变量: NEO4J_HOME: D:\neo4j-community-5.x.xJAVA_HOME: D:\neo4j-community-5.x.x\jdk(注意:需指向 JDK 目录)Path 变量…...
Centos操作系统安装及优化
Centos操作系统安装及优化 零、环境概述 主机名 centos版本 cpu 内存 Vmware版本 ip地址 test CentOS Linux release 7.6.1810 (Core) 2C 2G 15.5.1 10.0.0.10 一、介质下载 1、7.6版本下载 CentOS7.6标准版下载链接: https://archive.kernel.org/centos-vault/7.6.1810/i…...
