二、Spring Framework基础:IoC(控制反转)和DI(依赖注入)

devtools/2025/2/24 15:10:18/

Spring Core:深入理解 IoC 和 DI 原理

在 Java 开发中,Spring Framework 是一个极为重要的框架,而 IoC(控制反转)和 DI(依赖注入)是 Spring 的核心特性。它们不仅帮助开发者简化代码的复杂性,还极大地提高了代码的可维护性和可扩展性。本文将深入探讨 IoC 和 DI 的原理,并通过实际代码示例帮助你更好地理解。

1. IoC(控制反转)是什么?

1.1 定义

IoC(Inversion of Control,控制反转)是一种设计思想,用于降低代码之间的耦合度。它的核心思想是:将对象的创建和管理交给框架,而不是由程序员手动创建和管理

在传统的编程中,对象的创建和依赖关系是由程序员手动管理的。例如:

java">public class Client {private Service service = new Service();  // 客户端直接创建服务对象public void doSomething() {service.doService();}
}

在这个例子中,Client 类直接依赖于 Service 类,这种依赖关系是硬编码的,耦合度很高。如果 Service 类的实现发生变化,Client 类也需要修改。

而 IoC 的模式下,对象的创建和管理由 Spring 容器负责,客户端不再直接创建服务对象:

java">public class Client {private Service service;  // 服务对象由外部注入public Client(Service service) {this.service = service;}public void doSomething() {service.doService();}
}

在这种模式下,Client 类不再直接创建 Service 对象,而是通过外部注入的方式获取依赖。这种方式将对象的控制权从程序员手中“反转”到了框架,因此得名“控制反转”。

1.2 IoC 的优势

  1. 降低耦合度:对象之间的依赖关系由 Spring 容器管理,减少了类之间的直接依赖。
  2. 提高可维护性:代码更加清晰,依赖关系通过配置管理,易于修改和扩展。
  3. 便于测试:依赖关系可以通过 Mock 对象注入,便于单元测试。

2. DI(依赖注入)是什么?

2.1 定义

DI(Dependency Injection,依赖注入)是 IoC 的一种实现方式。它通过外部配置(如 XML、注解或 Java 配置类)将依赖关系注入到目标对象中。DI 的目的是让对象的依赖关系由外部容器管理,而不是由对象自己创建。

2.2 DI 的实现方式

DI 有以下几种常见的实现方式:

2.2.1 构造器注入

构造器注入是通过构造方法将依赖注入到目标对象中。这种方式的优点是依赖关系明确,且对象在创建时必须提供所有依赖,保证了对象的不可变性。

java">public class Client {private final Service service;public Client(Service service) {this.service = service;}public void doSomething() {service.doService();}
}
2.2.2 Setter 方法注入

Setter 方法注入是通过 Setter 方法将依赖注入到目标对象中。这种方式的优点是灵活性高,可以在对象创建后动态修改依赖。

java">public class Client {private Service service;public void setService(Service service) {this.service = service;}public void doSomething() {service.doService();}
}
2.2.3 字段注入

字段注入是通过注解直接将依赖注入到字段中。这种方式的优点是代码简洁,但缺点是破坏了封装性,且依赖关系不明显。

java">public class Client {@Autowiredprivate Service service;public void doSomething() {service.doService();}
}

2.3 DI 的优势

  1. 解耦:对象的依赖关系由外部容器管理,减少了类之间的直接依赖。
  2. 灵活性:依赖关系可以通过配置动态修改,而不必修改代码。
  3. 便于测试:依赖关系可以通过 Mock 对象注入,便于单元测试。

3. Spring IoC 容器的实现原理

Spring 的 IoC 容器是整个 Spring 框架的核心。它负责管理对象的生命周期、依赖注入和配置管理。

3.1 BeanFactory

BeanFactory 是 Spring IoC 容器的最基础实现,它提供了基本的依赖注入功能。它是一个接口,通常通过其实现类(如 ClassPathXmlApplicationContextFileSystemXmlApplicationContext)来使用。

java">import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;public class IoCDemo {public static void main(String[] args) {BeanFactory factory = new XmlBeanFactory(new ClassPathResource("beans.xml"));Service service = factory.getBean("service", Service.class);service.doService();}
}

3.2 ApplicationContext

ApplicationContextBeanFactory 的扩展,提供了更多高级功能,如事件传播、国际化支持等。它通常用于实际开发中。

java">import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class IoCDemo {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");Service service = context.getBean("service", Service.class);service.doService();}
}

3.3 Bean 的生命周期

Spring 容器管理 Bean 的生命周期,从创建到销毁的整个过程。以下是 Bean 生命周期的主要阶段:

  1. 实例化:Spring 容器调用无参构造方法实例化 Bean。
  2. 依赖注入:通过构造器或 Setter 方法注入依赖。
  3. 初始化:调用 @PostConstruct 注解的方法或实现 InitializingBean 接口的 afterPropertiesSet 方法。
  4. 使用:Bean 可以被其他 Bean 使用。
  5. 销毁:调用 @PreDestroy 注解的方法或实现 DisposableBean 接口的 destroy 方法。

3.4 配置方式

Spring 提供了多种配置方式,包括 XML 配置、注解配置和 Java 配置。

3.4.1 XML 配置
<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="service" class="com.example.Service"/>
</beans>
3.4.2 注解配置

Spring 提供了注解来简化配置,如 @Component@Service@Controller@Repository 等。

java">import org.springframework.stereotype.Service;@Service
public class Service {public void doService() {System.out.println("Service is running...");}
}

然后通过 @ComponentScan 扫描注解:

java">import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;@Configuration
@ComponentScan("com.example")
public class AppConfig {
}
3.4.3 Java 配置

除了注解,还可以通过 Java 配置类来定义 Bean:

java">import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class AppConfig {@Beanpublic Service service() {return new Service();}
}

4. 实现一个简单的 IoC 容器

为了更好地理解 Spring 的 IoC 容器,我们可以尝试实现一个简单的 IoC 容器。

4.1 定义一个简单的 Bean

java">public class Service {public void doService() {System.out.println("Service is running...");}
}

4.2 定义一个简单的 BeanFactory

java">import java.util.HashMap;
import java.util.Map;public class SimpleBeanFactory {private Map<String, Object> beans = new HashMap<>();public void registerBean(String name, Object bean) {beans.put(name, bean);}public Object getBean(String name) {return beans.get(name);}
}

4.3 测试 IoC 容器

java">public class IoCDemo {public static void main(String[] args) {SimpleBeanFactory factory = new SimpleBeanFactory();factory.registerBean("service", new Service());Service service = (Service) factory.getBean("service");service.doService();}
}

5. 构造器注入与 Setter 方法注入的对比

5.1 构造器注入

  • 优点
    • 依赖关系明确,对象在创建时必须提供所有依赖。
    • 保证对象的不可变性。
  • 缺点
    • 如果依赖关系较多,构造器参数会变得复杂。

5.2 Setter 方法注入

  • 优点
    • 灵活性高,可以在对象创建后动态修改依赖。
    • 代码简洁,易于理解。
  • 缺点
    • 对象的依赖关系不明显,可能导致对象在未完全初始化时被使用。

6. 实践案例:使用 Spring IoC 和 DI

6.1 定义一个业务逻辑类

java">import org.springframework.stereotype.Service;@Service
public class BusinessService {public void doSomething() {System.out.println("Business logic is running...");}
}

6.2 定义一个客户端类

java">import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;@Component
public class Client {private final BusinessService service;@Autowiredpublic Client(BusinessService service) {this.service = service;}public void execute() {service.doSomething();}
}

6.3 配置类

java">import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class AppConfig {@Beanpublic BusinessService businessService() {return new BusinessService();}@Beanpublic Client client(BusinessService service) {return new Client(service);}
}

6.4 测试

java">import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class IoCDemo {public static void main(String[] args) {ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);Client client = context.getBean(Client.class);client.execute();}
}

7. 总结

通过本文的介绍,你应该对 Spring 的 IoC 和 DI 原理有了更深入的理解。IoC 和 DI 是 Spring 框架的核心思想,它们通过将对象的创建和管理交给框架,极大地降低了代码的耦合度,提高了代码的可维护性和可扩展性。

在实际开发中,你可以根据需求选择合适的依赖注入方式(构造器注入、Setter 方法注入或字段注入),并利用 Spring 提供的强大功能来简化开发。

如果你在学习过程中有任何疑问,欢迎随时交流!


http://www.ppmy.cn/devtools/161386.html

相关文章

二分查找算法的全面解析C++

一、核心原理与特性 二分查找是一种**对数时间复杂度(O(log n))**的高效搜索算法46&#xff0c;需满足两个前提条件&#xff1a; 数据存储在连续内存空间&#xff08;如数组&#xff09;数据按升序/降序有序排列35 算法通过折半比较缩小搜索范围&#xff1a; 初始化左右边界…

网络安全入门 | TCP/IP协议栈核心协议详解(附攻防案例)

一、网络模型基础&#xff1a;OSI vs TCP/IP 1.1 经典OSI七层模型 7. 应用层 : HTTP/FTP/DNS 6. 表示层 : 数据加密/压缩 5. 会话层 : 建立/维护会话 4. 传输层 : TCP/UDP 3. 网络层 : IP/ICMP 2. 数据链路层 : ARP/PPP 1. 物理层 : 网线/光纤1.2 实际应用的TCP/I…

【js逆向入门】图灵爬虫练习平台 第七题

地址&#xff1a;aHR0cHM6Ly9zdHUudHVsaW5ncHl0b24uY24vcHJvYmxlbS1kZXRhaWwvNy8 打开f12&#xff0c;立马进入了debugger&#xff0c;处理一下&#xff0c;过debugger 请求接口&#xff0c;page代表第几页&#xff0c;关键是找到x的生成逻辑 再看请求标头&#xff0c;有关键参…

【Ubuntu】GPU显存被占用,但显示没有使用GPU的进程

文章目录 一、问题描述二、解决方案2.1 寻找问题进程2.2 尝试杀死相关进程2.3 投放核弹&#xff0c;一键全杀2.4 再次查看GPU使用情况 参考资料 一、问题描述 今天使用服务器的时候发现gpu被占了很多内存&#xff0c;但是使用 nvidia-smi 命令并没有发现占这么多显存的进程&am…

从零开始玩转TensorFlow:小明的机器学习故事 4

探索深度学习 1 场景故事&#xff1a;小明的灵感 前不久&#xff0c;小明一直在用传统的机器学习方法&#xff08;如线性回归、逻辑回归&#xff09;来预测学校篮球比赛的胜负。虽然在朋友们看来已经很不错了&#xff0c;但小明发现一个问题&#xff1a;当比赛数据越来越多、…

Webpack的持久化缓存机制具体是如何实现的?

Webpack 的持久化缓存机制是 Webpack 5 引入的一项重要特性&#xff0c;旨在提高构建速度和性能。通过将构建结果缓存到磁盘上&#xff0c;Webpack 可以在后续构建中重用先前的结果&#xff0c;减少不必要的重新计算。以下是持久化缓存机制的具体实现和工作原理。 一、持久化缓…

基于CNN的FashionMNIST数据集识别2——模型训练

源码 import copy import timeimport torch from torchvision.datasets import FashionMNIST from torchvision import transforms import torch.utils.data as Data import numpy as np import matplotlib.pyplot as plt from model import LeNet import torch.nn as nn impo…

解决videojs在ios端视频无法播放的问题

解决videojs在ios端视频无法播放的问题 问题描述&#xff1a;问题原因116为本地环境&#xff0c;为无缓存37为测试服务器 解决方法 问题描述&#xff1a; 在做多端嵌入的H5页面时&#xff0c;通过videojs插件做视频的播放&#xff0c;发现在web网页&#xff0c;andriod的app端…