基于本文回答

播面 播面

文图音视,全方位拆解八股文
0
评论

Spring 中的IoC(控制反转)和 DI(依赖注入)

知识点图片

在 Spring 框架中,IoC(控制反转)DI(依赖注入) 是其最核心的基石。理解了它们,就等于掌握了 Spring 的灵魂。

为了让你通俗易懂地理解,我们将从概念、关系、实现方式以及优势四个方面进行拆解。


一、 什么是 IoC(Inversion of Control - 控制反转)?

IoC 是一种设计思想,而不是一种具体的技术。

  • 传统开发模式(正转):
    假设对象 A 需要使用对象 B 的功能,通常会在对象 A 的代码里直接 new 一个对象 B。
    控制权在哪? 在对象 A 手里。A 负责决定什么时候创建 B,怎么创建 B。
    缺点: A 和 B 强耦合(绑死在一起了),如果 B 的构造方式变了,A 的代码也要跟着改。

  • IoC 开发模式(反转):
    对象 A 需要对象 B,但 A 不再自己去 new 对象 B 了,而是把创建和管理对象 B 的权力交给了 Spring 容器。Spring 容器会在合适的时机,主动把对象 B 交给(注入给)对象 A。
    控制权在哪? 反转给了第三方(Spring 容器)。
    “反转”的到底是什么?获取依赖对象的控制权被反转了。

🍔 通俗比喻:
传统模式(自己做饭): 你想吃汉堡,你需要自己去买面包、买肉饼、生火、煎肉、组装(自己 new 对象)。
IoC 模式(点外卖): 你想吃汉堡,你只需要给外卖平台(Spring 容器)下个单(声明需求),外卖平台会让商家做好,派骑手送到你手里。你不需要关心汉堡是怎么做出来的。


二、 什么是 DI(Dependency Injection - 依赖注入)?

DI 是 IoC 思想的具体实现方式。

  • 依赖(Dependency): 对象 A 运行需要对象 B,我们就说 A 依赖于 B。
  • 注入(Injection): Spring 容器在创建对象 A 时,自动将对象 A 所需要的对象 B 传递(注入)给它。

IoC 和 DI 的关系:
IoC 是目标和思想,DI 是手段和实现。 (就像“我要去北京”是 IoC,“坐高铁去”是 DI)。


三、 Spring 中实现 DI(依赖注入)的三种方式

在 Spring 中,把对象放进容器叫做 Bean,Spring 提供了三种主要的方式来将依赖的 Bean 注入到目标代码中:

1. 属性注入(Field Injection)- 最常见

通过在类的成员变量上直接加 @Autowired@Resource 注解。

java
@Service
public class UserService {
    // Spring 会自动找到 UserRepository 的实例并赋值给这个变量
    @Autowired 
    private UserRepository userRepository;

    public void getUser() {
        userRepository.findUser();
    }
}
  • 优点: 代码极其简洁。
  • 缺点: 隐藏了依赖关系,离开 Spring 容器后难以进行单元测试(容易报空指针)。

2. 构造器注入(Constructor Injection)- Spring 官方强推

通过类的构造方法来传入依赖对象。如果类只有一个构造方法,Spring 会隐式地自动注入(@Autowired 可以省略)。

java
@Service
public class UserService {
    private final UserRepository userRepository;

    // 依赖通过构造方法传入
    @Autowired // 如果只有一个构造器,这个注解可省略
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}
  • 优点: 保证依赖不可变(可以加 final),确保对象被实例化时依赖已经准备好,非常方便脱离 Spring 进行单元测试(直接 new 并传入 mock 对象即可)。

3. Setter 方法注入(Setter Injection)

通过提供 Setter 方法,并在方法上加 @Autowired

java
@Service
public class UserService {
    private UserRepository userRepository;

    @Autowired
    public void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}
  • 优点: 适合注入可选的依赖(允许依赖为空或后续更改)。

四、 Spring 是如何知道该创建/注入哪些对象的?

Spring 容器需要知道你的“点餐需求”,这就需要配置。现代 Spring (Spring Boot) 主要通过注解来实现:

  • 把对象交给 Spring 容器管理(声明 Bean):

    • @Component:通用的组件。
    • @Service:用于业务逻辑层(Service 层)。
    • @Repository:用于数据访问层(DAO 层)。
    • @Controller:用于控制层(接收 HTTP 请求)。
    • @Bean:通常写在 @Configuration 类里,用于将第三方库的对象放入容器。
  • 从 Spring 容器取出对象(注入依赖):

    • @Autowired:Spring 提供的,默认按类型(Type)匹配。
    • @Resource:JDK 提供的,默认按名称(Name)匹配。

五、 为什么非要用 IoC 和 DI?(核心优势)

  1. 极大地降低了代码耦合度(解耦):
    对象之间不再有硬编码的创建关系。如果 UserRepository 的实现从 MySQL 换成了 Oracle,UserService 的代码一行都不需要改,只需要在配置或注解里调整即可。
  2. 提高了代码的可测试性:
    在写单元测试时,你可以轻松地自己创建一个假的(Mock)依赖对象传进去,而不需要启动整个数据库或复杂的环境。
  3. 集中管理对象的生命周期:
    Spring 容器负责对象的创建、初始化、销毁。默认情况下,Spring 中的 Bean 都是单例(Singleton)的,这极大地节省了内存,提高了性能。开发者再也不用管各种麻烦的生命周期问题了。

总结:
IoC 告诉你:“别自己去 new 对象了,让 Spring 容器帮你管吧。”
DI 告诉你:“你要什么对象,直接在参数或属性上声明一下,Spring 容器会塞给你的。”

00:00
00:00