Spring IOC详解


一、什么是 IoC(控制反转)

一句话理解

对象的创建和依赖关系的管理,不再由程序员控制,而是交给 Spring 容器管理。


二、先看“没有 IoC”的写法

1
2
3
class UserService {
private UserDao userDao = new UserDao();
}

问题

  • 强耦合(UserService 依赖具体实现)
  • 不利于扩展(换实现类要改代码)
  • 不方便测试(无法 mock)

三、有 IoC 后的写法

1
2
3
4
5
6
7
class UserService {
private UserDao userDao;

public UserService(UserDao userDao) {
this.userDao = userDao;
}
}

现在:

  • UserDao 不是自己 new 的
  • 而是由外部(Spring)传进来

四、什么是“控制反转”

传统方式

1
程序员 → 控制对象创建

IoC

1
2
Spring容器 → 控制对象创建
程序员 → 只负责使用

控制权“反转”了!


五、Spring IoC 解决了什么问题?

Spring 本质就是一个对象工厂

它负责:

  1. 创建对象(Bean)
  2. 管理对象生命周期
  3. 维护依赖关系
  4. 自动注入依赖

IoC的思想就是两方之间不互相依赖,由第三方来管理相关资源,这样做可以:

  • 对象之间的耦合度或者说是依赖程度降低;
  • 资源变得容易管理。

六、核心概念:Bean

Bean = 被 Spring 管理的对象

1
2
@Component
class UserService {}

这个类就会被 Spring 扫描并创建成 Bean


七、依赖注入(DI)

IoC 的实现方式就是 DI(Dependency Injection)

常见三种方式:

7.1 构造器注入(推荐⭐)

1
2
3
4
5
6
7
8
9
@Service
class UserService {
private final UserDao userDao;

@Autowired
public UserService(UserDao userDao) {
this.userDao = userDao;
}
}

7.2 属性注入(不推荐)

1
2
@Autowired
private UserDao userDao;

7.3 setter 注入

1
2
3
4
@Autowired
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}

八、IoC 的底层原理(面试重点🔥)

8.1 读取配置

  • XML / 注解 / Java Config

8.2 BeanDefinition

  • 把类的信息解析成 Bean 定义

8.3 反射创建对象

1
Class.forName().newInstance()

8.4 依赖注入

  • 通过构造器 / setter / 字段赋值

8.5 放入容器(Map)

1
Map<String, Object> singletonObjects

九、IoC 带来的好处

优点 说明
解耦 不依赖具体实现
可扩展 易替换实现
易测试 支持 mock
生命周期管理 Spring 统一管理
提高开发效率 少写重复代码

十、一个形象比喻

传统开发

你自己做饭:

  • 买菜
  • 做饭
  • 洗碗

IoC

你点外卖:

  • 你只负责吃
  • 厨房(Spring)帮你做好一切

十一、总结(面试速答模板)

IoC(控制反转)是指将对象的创建和依赖关系的管理交给 Spring 容器来完成,而不是由程序员手动 new 对象。
传统的开发方式往往是在类A中手动new一个B对象,使用IoC思想的开发方式是:不使用new关键字来创建对象,而是通过IoC容器来帮助我们实例化对象,需要那个对象直接去IoC容器中取即可。
Spring 通过依赖注入(DI)实现 IoC,常见方式包括构造器注入、setter 注入和字段注入。
IoC 的本质是降低耦合,提高代码的可维护性和可扩展性。

可以。直接用一个真实开发中非常常见的场景来说明:👉「用户下单 + 支付方式切换」。


十二、场景说明使用IoC的好处

12.1 场景:电商系统支付模块

你要写一个 OrderService,负责下单并调用支付。

系统支持:

  • 支付宝
  • 微信支付
  • 以后可能还要加:银行卡、Apple Pay…

12.2 不使用 IoC 的写法(问题很典型)

1
2
3
4
5
6
class OrderService {
public void createOrder() {
AlipayService alipayService = new AlipayService();
alipayService.pay();
}
}

🚨 问题分析

  1. 强耦合
1
OrderService → 绑定 AlipayService

如果改成微信支付:

1
WeChatPayService weChatPayService = new WeChatPayService();

必须修改源码

  1. 扩展性差

如果要支持多种支付:

1
2
3
4
5
if (type.equals("alipay")) {
new AlipayService().pay();
} else if (type.equals("wechat")) {
new WeChatPayService().pay();
}

违反 开闭原则(OCP)

  1. 测试困难

你想测试:

  • 成功支付
  • 支付失败

但你没法替换 AlipayService(不能 mock)

12.3 使用 IoC + DI 的写法

  1. 第一步:定义接口(解耦核心)
1
2
3
interface PayService {
void pay();
}
  1. 第二步:不同实现
1
2
3
4
5
6
7
8
9
10
11
12
13
@Component
class AlipayService implements PayService {
public void pay() {
System.out.println("支付宝支付");
}
}

@Component
class WeChatPayService implements PayService {
public void pay() {
System.out.println("微信支付");
}
}
  1. 第三步:OrderService 不再自己创建对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Service
class OrderService {

private final PayService payService;

@Autowired
public OrderService(PayService payService) {
this.payService = payService;
}

public void createOrder() {
payService.pay();
}
}

12.4 IoC 带来的实际好处

  1. 解耦(最核心)
1
OrderService → 依赖接口 PayService

不关心具体实现是谁

  1. 扩展性极强

新增一个支付方式:

1
2
3
4
@Component
class ApplePayService implements PayService {
public void pay() {}
}

不需要修改 OrderService 一行代码

  1. 支持动态切换(配置驱动)

比如:

1
@Qualifier("weChatPayService")

直接切换实现

甚至可以:

  • 根据配置文件
  • 根据环境(dev/test/prod)
  1. 更容易测试
1
2
3
4
5
class MockPayService implements PayService {
public void pay() {
System.out.println("模拟支付");
}
}

测试时直接注入 mock:

1
new OrderService(new MockPayService());
  1. 生命周期统一管理

Spring 可以控制:

  • 单例 / 多例
  • 初始化
  • 销毁

你不用手动管理对象

12.5 核心变化总结

维度 不用 IoC 使用 IoC
对象创建 自己 new Spring 管
依赖关系 写死 自动注入
扩展性
测试 困难 简单
耦合度

12.6 一句话总结(面试用)

在实际开发中,比如支付模块,如果不使用 IoC,业务类会直接依赖具体实现,导致强耦合、难扩展。而使用 IoC 后,通过依赖注入依赖接口,Spring 负责实例创建和注入,使系统具备良好的解耦性、扩展性和可测试性。