让我通过一个具体的例子来说明控制反转(IoC)是如何"反转"的。
在传统编程中(没有 IoC 之前):
public class UserService {
// UserService 直接创建 UserDao
private UserDao userDao = new UserDaoImpl();
public User getUser(Long id) {
return userDao.findById(id);
}
}
使用 Spring IoC 之后:
public class UserService {
// UserDao 由容器注入,不再由 UserService 创建
@Autowired
private UserDao userDao;
public User getUser(Long id) {
return userDao.findById(id);
}
}
这里的"反转"体现在三个方面:
- 控制权的反转
- 传统:对象的创建和依赖关系由开发者在代码中直接控制
- IoC:对象的创建和依赖关系交给 Spring 容器来控制
- 依赖方式的反转
- 传统:组件主动去创建依赖的对象
- IoC:组件被动等待容器注入依赖的对象
- 依赖查找方式的反转
- 传统:组件主动去查找它依赖的对象
- IoC:容器将依赖对象主动注入到组件中
IoC 的好处:
- 降低代码耦合度
- 更容易进行单元测试(可以方便地注入 mock 对象)
- 更好的模块化
- 更容易替换组件的实现
这种反转对开发的影响:
// 配置类
@Configuration
public class AppConfig {
@Bean
public UserDao userDao() {
return new UserDaoImpl();
}
@Bean
public UserService userService() {
return new UserService();
}
}
通过这种方式,我们可以:
- 在配置文件中轻松更换实现类
- 统一管理对象的生命周期
- 方便地配置对象的作用域
- 实现 AOP 等高级特性