Spring 源码学习-理解IOC和ID
大约 3 分钟
理解IoC(控制反转)和DI(依赖注入)是掌握Spring框架的核心基础。它们是一个概念的两种不同角度的表述,以下用通俗易懂的方式解释:
一、生活化比喻理解
场景:点餐与厨房
- 传统模式(无IoC)
你(开发者)自己买菜、切菜、炒菜(直接new
对象)、摆盘。
问题:流程全掌控,但换食材或调整口味时要修改所有步骤。 - IoC模式
你去餐厅点菜(定义需求),厨房(IoC容器)自动完成配菜、烹饪。
关键:控制权从你转移到了厨房(容器)。
DI(依赖注入)
服务员(容器)将做好的菜品(对象实例)端到你面前的过程,就是依赖注入。
"注入"的本质:把对象依赖的其他对象传递给它,而不是让它自己创建。
二、技术本质解析
1. IoC(Inversion of Control)控制反转
- 核心思想:将对象的创建、生命周期管理权交给容器,而非程序员手动
new
对象。 - 传统方式:
// 开发者自己控制对象创建
UserService userService = new UserServiceImpl();
userService.setUserDao(new UserDaoImpl());
- IoC方式:
// 向容器"声明"需要什么,由容器创建和注入
@Autowired
private UserService userService;
2. DI(Dependency Injection)依赖注入
- IoC的具体实现方式:由容器动态地将依赖关系注入到对象中。
- 三种注入方式:
- 构造器注入(最推荐):
public class UserService {
private final UserDao userDao;
// 容器自动注入
public UserService(UserDao userDao) {
this.userDao = userDao;
}
}
- **Setter注入**:
public class UserService {
private UserDao userDao;
// 容器调用setter方法
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
- **接口注入**(较少用):通过实现特定接口完成注入。
三、通过代码对比理解
传统硬编码方式(紧耦合)
public class OrderService {
// 自己创建依赖对象
private PaymentService paymentService = new AlipayService();
public void pay() {
paymentService.processPayment();
}
}
问题:若想切换为微信支付,必须修改源代码。
IoC+DI方式(解耦)
public class OrderService {
// 声明依赖但不创建
private final PaymentService paymentService;
// 容器通过构造器注入
public OrderService(PaymentService paymentService) {
this.paymentService = paymentService;
}
}
// 配置类(定义Bean关系)
@Configuration
public class AppConfig {
@Bean
public PaymentService paymentService() {
return new WechatPayService(); // 只需修改配置即可切换实现
}
}
四、Spring中的具体实现
1. XML配置方式(早期)
<!-- 定义Bean -->
<bean id="userDao" class="com.example.UserDaoImpl"/>
<bean id="userService" class="com.example.UserServiceImpl">
<!-- 依赖注入 -->
<property name="userDao" ref="userDao"/>
</bean>
2. 注解方式(现代主流)
@Repository // 标记为Dao层Bean
public class UserDaoImpl implements UserDao {}
@Service // 标记为Service层Bean
public class UserServiceImpl implements UserService {
@Autowired // 自动注入依赖
private UserDao userDao;
}
五、关键理解要点
- IoC是设计目标,DI是实现手段
- IoC是思想(控制权反转),DI是具体落地方式(如何注入依赖)。
- 为什么需要IoC/DI?
- 解耦:对象间不再直接依赖具体实现
- 可测试性:方便Mock依赖进行单元测试
- 可维护性:修改依赖时无需改动业务代码
- 灵活性:通过配置快速切换实现类
- 容器的作用
- 创建并管理Bean的生命周期
- 解析依赖关系(如@Autowired)
- 处理AOP代理、事务管理等扩展
六、常见误区
- IoC ≠ DI
DI是IoC的一种实现方式,除此之外还有依赖查找(Dependency Lookup)。 - @Autowired不是DI的全部
注解只是标记,真正的依赖解析和注入由BeanPostProcessor
等机制完成。 - 容器不是魔法
理解背后的BeanFactory
、ApplicationContext
等核心接口,才能避免“玄学编程”。
掌握IoC/DI后,你会发现Spring的@Bean
、@ComponentScan
、@Conditional
等机制都变得自然通透。这是理解AOP、事务管理、Spring Boot自动装配等高级特性的基石。