跳至主要內容

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;
}

五、关键理解要点

  1. IoC是设计目标,DI是实现手段
    • IoC是思想(控制权反转),DI是具体落地方式(如何注入依赖)。
  2. 为什么需要IoC/DI?
    • 解耦:对象间不再直接依赖具体实现
    • 可测试性:方便Mock依赖进行单元测试
    • 可维护性:修改依赖时无需改动业务代码
    • 灵活性:通过配置快速切换实现类
  3. 容器的作用
    • 创建并管理Bean的生命周期
    • 解析依赖关系(如@Autowired)
    • 处理AOP代理、事务管理等扩展

六、常见误区

  • IoC ≠ DI
    DI是IoC的一种实现方式,除此之外还有依赖查找(Dependency Lookup)。
  • @Autowired不是DI的全部
    注解只是标记,真正的依赖解析和注入由BeanPostProcessor等机制完成。
  • 容器不是魔法
    理解背后的BeanFactoryApplicationContext等核心接口,才能避免“玄学编程”。

掌握IoC/DI后,你会发现Spring的@Bean@ComponentScan@Conditional等机制都变得自然通透。这是理解AOP、事务管理、Spring Boot自动装配等高级特性的基石。