深入理解 Spring 框架中的 IOC 容器

news2025/3/25 12:07:00

一、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 管理操作的两种方式

  1. 基于 xml 配置文件的方式
    • 创建对象:在 xml 配置文件中,通过<bean>标签来配置要创建的对象。例如<bean id="demo" class="com.qcby.service.Demo" />,其中id属性指定了对象在容器中的唯一标识,class属性指定了对象所属的类。在创建对象时,默认会执行无参构造方法来完成对象的创建。
    • 注入属性
      • set 方法注入值:在类中编写属性,并为其提供对应的 set 方法。然后在配置文件中使用<property>标签来完成属性值的注入。例如:
<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>
  1. 基于注解的方式
    • 什么是注解:注解是代码中的特殊标记,格式为@注解名称(属性名称=属性值,属性名称=属性值...)。它可以作用在类、方法、属性等上面,目的是简化 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 {
    @Autowired
    private 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 纯注解的方式

在微服务架构开发中,纯注解方式愈发重要,因为它旨在替换掉所有的配置文件,进一步简化开发流程。不过,使用纯注解需要编写配置类。

  1. 常用的注解总结
    • @Configuration:声明该类是一个配置类,用于替代传统的 XML 配置文件。
    • @ComponentScan:用于扫描指定的包结构,将包内符合条件的类纳入 IOC 容器管理。
  2. 具体实现示例
    • 编写实体类
@Component
public class Order {
    @Value("北京")
    private String address;
    @Override
    public 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 {
    @Test
    public 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 容器的精髓,并在开发实践中发挥其最大价值。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2320551.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2025前端面试题记录

vue项目目录的执行顺序是怎么样的&#xff1f; 1、package.json   在执行npm run dev时&#xff0c;会在当前目录寻找package.json文件&#xff0c;此文件包含了项目的名称版本、项目依赖等相关信息。 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 αn​an​ibn​ 为复数&#xff0c;与高等数学中无穷级数的分析方式类似&#xff0c;也是通过和函数来研究级数的收敛…

高频面试题(含笔试高频算法整理)基本总结回顾67

干货分享&#xff0c;感谢您的阅读&#xff01; &#xff08;暂存篇---后续会删除&#xff0c;完整版和持续更新见高频面试题基本总结回顾&#xff08;含笔试高频算法整理&#xff09;&#xff09; 备注&#xff1a;引用请标注出处&#xff0c;同时存在的问题请在相关博客留言…

Kafka--常见问题

1.为什么要使用 Kafka&#xff0c;起到什么作用 Kafka是一个高吞吐量、分布式、基于发布订阅的消息系统&#xff0c;它主要用于处理实时数据流 Kafka 设计上支持高吞吐量的消息传输&#xff0c;每秒可以处理数百万条消息。它能够在处理大量并发请求时&#xff0c;保持低延迟和…

优选算法的睿智之林:前缀和专题(一)

专栏&#xff1a;算法的魔法世界 个人主页&#xff1a;手握风云 目录 一、前缀和 二、例题讲解 2.1. 一维前缀和 2.2. 二维前缀和 2.3. 寻找数组的中心下标 2.4. 除自身以外数组的乘积 一、前缀和 前缀和算法是一种用于处理数组或序列数据的算法&#xff0c;其核心思想是…

【清华大学】AIGC发展研究(3.0版)

目录 AIGC发展研究报告核心内容一、团队简介二、AI哲学三、国内外大模型四、生成式内容&#xff08;一&#xff09;文本生成&#xff08;二&#xff09;图像生成&#xff08;三&#xff09;音乐生成&#xff08;四&#xff09;视频生成 五、各行业应用六、未来展望 AIGC发展研究…

JavaSE1.0(基础语法之运算符)

算术运算符 基础运算之加 减 乘 除 取余&#xff08; - * / %&#xff09; 运算符之相加&#xff08; &#xff09; 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);//…

蓝桥与力扣刷题(蓝桥 数的分解)

题目&#xff1a;把 2019分解成 3个各不相同的正整数之和&#xff0c;并且要求每个正整数都不包含数字 2 和 4&#xff0c;一共有多少种不同的分解方法&#xff1f; 注意交换 3 个整数的顺序被视为同一种方法&#xff0c;例如 1000100118和 1001100018 被视为同一种。 解题思…

Java IO 流:从字节到字符再到Java 装饰者模式(Decorator Pattern),解析与应用掌握数据流动的艺术

在 Java 编程中&#xff0c;IO&#xff08;输入输出&#xff09;流是处理数据输入输出的核心工具。无论是读取文件、网络通信&#xff0c;还是处理用户输入&#xff0c;IO 流都扮演着重要角色。本文将深入探讨 Java IO 流的核心概念、分类、经典代码实例及其应用场景&#xff0…

爬虫案例-爬取某站视频

文章目录 1、下载FFmpeg2、爬取代码3、效果图 1、下载FFmpeg FFmpeg是一套可以用来记录、转换数字音频、视频&#xff0c;并能将其转化为流的开源计算机程序。 点击下载: ffmpeg 安装并配置 FFmpeg 步骤&#xff1a; 1.下载 FFmpeg&#xff1a; 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系统基于流&#xff08;Stream&#xff09;​的概念&#xff0c;分为三层结构&#xff1a; ​流对象​&#xff08;如cin, cout, fstream&#xff09;​流缓冲区​&#xff08;streambuf&#xff0c;负责底层数据处理&#xff09;​数据源/目的…

企业级前端架构设计与实战

一、架构设计核心原则 1.1 模块化分层架构 典型目录结构&#xff1a; src/├── assets/ # 静态资源├── components/ # 通用组件├── pages/ # 页面模块├── services/ # API服务层├── store/ # 全局状态管理├── uti…

从入门到精通【MySQL】 CRUD

文章目录 &#x1f4d5;1. Create 新增✏️1.1 单行数据全列插入✏️1.2 单行数据指定列插入✏️1.3 多行数据指定列插入 &#x1f4d5;2. Retrieve 检索✏️2.1 全列查询✏️2.2 指定列查询✏️2.3 查询字段为表达式✏️2.4 为查询结果指定别名✏️2.5 结果去重查询 &#x1f…

08_双向循环神经网络

双向网络 概念 双向循环神经网络&#xff08;Bidirectional Recurrent Neural Network, BiRNN&#xff09;通过同时捕捉序列的正向和反向依赖关系&#xff0c;增强模型对上下文的理解能力。与传统的单向网络不同,BIRNN 能够同时从过去和未来的上下文信息中学习,从而提升模型的…

2025年Postman的五大替代工具

虽然Postman是一个广泛使用的API测试工具&#xff0c;但许多用户在使用过程中会遇到各种限制和不便。因此&#xff0c;可能需要探索替代解决方案。本文介绍了10款强大的替代工具&#xff0c;它们能够有效替代Postman&#xff0c;成为你API测试工具箱的一部分。 什么是Postman&…

(四)---四元数的基础知识-(定义)-(乘法)-(逆)-(退化到二维复平面)-(四元数乘法的导数)

使用四元数的原因 最重要的原因是因为传感器的角速度计得到的是三个轴的角速度, 这三个轴的角速度合成一个角速度矢量, 结果就是在微小时间内绕着这个角速度矢量方向为轴旋转一定角度. 截图来源网址四元数 | Crazepony开源四轴飞行器

汇能感知高品质的多光谱相机VSC02UA

VSC02UA概要 VSC02UA是一款高品质的200万像素的光谱相机&#xff0c;适用于工业检测、农业、医疗等领域。VSC02UA 包含 1600 行1200 列有源像素阵列、片上 10 位 ADC 和图像信号处理器。它带有 USB2.0 接口&#xff0c;配合专门的电脑上位机软件使用&#xff0c;可进行图像采集…

Blazor+PWA技术打造全平台音乐播放器-从音频缓存到离线播放的实践之路

开局三张图… 0.起源 主要是自己现在用的是苹果手机&#xff0c;虽然手机很高级&#xff0c;但是想听自己喜欢的歌曲确是不容易&#xff0c;在线app都要付费&#xff0c;免费的本地播放器都不太好用&#xff08;收费的也不太行&#xff09;&#xff0c;基础功能都不满足。此外…

使用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 变量…