深入理解IOC与DI
掌握控制反转(IOC)和依赖注入(DI)是理解Spring Boot框架的关键
一、传统开发模式的痛点
典型场景:用户服务调用用户仓库
// 传统实现(紧耦合)
public class UserService {private UserRepository userRepo = new UserRepository();public User findUser(Long id) {return userRepo.findById(id);}
}
问题分析:
- UserService 主动创建UserRepository实例(控制权在Service)
- 变更实现类(如MysqlUserRepo→MongoUserRepo)需修改源码
- 单元测试需真实数据库难以Mock)
二、IOC:控制反转(Inversion of Control)
核心思想:将对象创建权交给容器
// Spring IOC实现
@Component
public class UserService {private UserRepository userRepo;// 不再主动创建对象
}
运作原理:
- Spring启动时扫描@Component, @Service等注解
- 自动创建Bean实例存入IOC容器(内存数据库)
- 容器管理Bean的生命周期(创建→装配→销毁)
IOC优势:
-
解耦:调用者与被调用者分离
-
资源集中管理:容器统一分配对象
-
可扩展性:方便替换实现类
三、DI:依赖注入(Dependency Injection)
核心思想:由容器动态注入依赖对象
Spring提供三种主流注入方式:
1. 构造器注入(Spring官方推荐)
@Service
public class UserService {private final UserRepository userRepo;// 容器自动注入依赖@Autowiredpublic UserService(UserRepository userRepo) {this.userRepo = userRepo;}
}
优势:
-
保证依赖不可变(final关键字)
-
避免循环依赖问题
-
明确依赖关系
2. Setter注入
@Service
public class UserService {private UserRepository userRepo;@Autowiredpublic void setUserRepo(UserRepository userRepo) {this.userRepo = userRepo;}
}
适用场景:可选依赖或需要重新配置的依赖
3. 字段注入
@Service
public class UserService {@Autowired // 直接注入字段private UserRepository userRepo;
}
注意:虽然简洁,但不利于单元测试(需通过反射注入)
四、IOC容器工作流程
sequenceDiagram启动类->>IOC容器: @SpringBootApplicationIOC容器->>扫描器: 组件扫描扫描器->>Bean工厂: 发现@Component/@BeanBean工厂->>依赖解析: 分析依赖关系依赖解析->>DI引擎: 创建依赖图谱DI引擎->>Bean实例化: 构造器+Setter注入Bean实例化->>IOC容器: 托管BeanIOC容器->>应用: 提供运行时Bean
关键注解解析
注解 | 作用 | 示例 |
@Component | 通用Bean声明 | @Component public class MyBean |
@Autowired | 自动依赖注入 | @Autowired private Dependency dep; |
@Qualifier | 解决歧义注入 | @Qualifier("mysqlImpl") |
@Primary | 设置首选Bean | @Bean @Primary |
@Lazy | 延迟初始化 | @Lazy @Service |
五、彻底理解IOC与DI的关系
概念对比表
概念 | 控制反转 (IOC) | 依赖注入 (DI) |
核心思想 | 转移对象控制权 | 实现控制反转的具体手段 |
实现方式 | 容器托管Bean | 通过构造器/Setter注入依赖 |
关注点 | 谁控制对象的生命周期 | 如何传递依赖对象 |
关系 | 设计思想 | 具体实现技术 |
重要结论:
DI是IOC的一种实现方式(还有服务定位器等),Spring选择DI实现IOC
六、最佳实践与常见误区
正确姿势
// 1. 使用构造器注入 + final
@Service
public class OrderService {private final PaymentService paymentService;@Autowired // Spring 4.3+可省略public OrderService(PaymentService paymentService) {this.paymentService = paymentService;}
}// 2. 面向接口编程
public interface UserRepository {...}
@Repository
public class JpaUserRepository implements UserRepository {...}// 3. 使用@Configuration声明配置类
@Configuration
public class AppConfig {@Beanpublic DataSource dataSource() {return new HikariDataSource();}
}
典型错误
// 反例1:在Bean中主动new对象
@Service
public class UserService {private UserRepository repo = new UserRepository(); // 破坏IOC
}// 反例2:循环依赖
@Service
public class A {@Autowired B b;
}
@Service
public class B {@Autowired A a; // 启动报错
}
七、高频面试题速答
Q1:IOC和DI有什么区别?
答:IOC是设计目标(控制权反转),DI是实现手段(依赖注入)
Q2:@Autowired和@Resource有何不同?
答:
- @Autowired按类型注入,支持@Primary和@Qualifier
- @Resource按名称注入(JDK标准注解)
Q3:如何解决多个同类型Bean的冲突?
答:三种方案:
- 用@Qualifier("beanName")指定名称
- 在目标Bean添加@Primary注解
- 使用@Resource(name="beanName")
最后总结:
- IOC = 容器掌控对象生命周期(程序→容器)
- 💉 DI = 容器自动注入依赖(容器→程序)
- 🔧 掌握@Autowired+构造器注入是Spring Boot开发基础
- ⚡ 理解二者关系即掌握Spring框架的核心设计哲学