Spring 的 IOC(控制反转)是框架的核心机制,用于管理对象的创建和依赖注入,通过将控制权从应用程序代码转移到容器,实现组件间的解耦。以下是详细解析:
1. IOC 核心概念
-
控制反转(Inversion of Control)
传统开发中,对象主动创建依赖,导致紧耦合。IOC 将对象创建和依赖管理的控制权交给 Spring 容器,由容器负责实例化、配置和组装对象。
-
依赖注入(Dependency Injection, DI)
IOC 的具体实现方式,容器通过构造器、Setter 方法或字段注入,将依赖对象传递给目标组件。
2. IOC 容器工作机制
(1) 容器初始化
- 配置源 :XML 文件、Java 注解(如
@Component
)或 Java 配置类(@Configuration
)。 - 步骤 :
- 加载配置:容器读取配置元数据。
- 创建 Bean 定义:解析配置,生成 Bean 的定义信息(类名、作用域、依赖关系等)。
- 实例化 Bean:根据定义创建 Bean 实例(通过反射或无参构造函数)。
- 依赖注入:解析并注入 Bean 的依赖。
- 初始化回调 :调用
@PostConstruct
或init-method
指定的方法。
(2) Bean 生命周期
- 实例化:通过反射创建对象。
- 属性填充:注入依赖。
- 初始化:执行初始化方法。
- 使用:Bean 处于可用状态。
- 销毁 :容器关闭时调用
@PreDestroy
或destroy-method
。
(3) Bean 作用域
作用域 | 描述 |
---|---|
Singleton | 默认作用域,容器中仅存在一个实例。 |
Prototype | 每次请求创建新实例。 |
Request | 单个 HTTP 请求生命周期内有效(Web 应用)。 |
Session | 单个用户会话生命周期内有效(Web 应用)。 |
Application | ServletContext 生命周期内有效(Web 应用)。 |
3. 依赖注入方式
(1) 构造器注入
-
优点:强制依赖不可变,确保 Bean 完整初始化。
-
代码示例 :
java@Service public class UserService { private final UserRepository userRepository; @Autowired public UserService(UserRepository userRepository) { this.userRepository = userRepository; } }
(2) Setter 方法注入
-
优点:可选依赖,灵活配置。
-
代码示例 :
javapublic class OrderService { private PaymentService paymentService; @Autowired public void setPaymentService(PaymentService paymentService) { this.paymentService = paymentService; } }
(3) 字段注入
-
优点:代码简洁。
-
缺点:不易测试,隐藏依赖关系。
-
代码示例 :
java@Service public class ProductService { @Autowired private InventoryService inventoryService; }
4. 配置方式对比
配置方式 | 特点 | 适用场景 |
---|---|---|
XML 配置 | 集中管理,灵活但繁琐。 | 旧项目迁移,复杂 Bean 配置。 |
Java 注解 | 简洁直观,结合组件扫描(@ComponentScan )。 |
现代应用,快速开发。 |
Java 配置类 | 类型安全,IDE 友好(@Configuration + @Bean )。 |
需要编程式配置或条件化 Bean。 |
5. 自动装配(Autowiring)
- 原理 :容器根据类型(
byType
)或名称(byName
)自动解析依赖。 - 注解 :
@Autowired
:按类型注入,可结合@Qualifier
指定 Bean 名称。@Resource
:按名称注入(JSR-250 标准)。
示例:
java
@Service
public class OrderService {
@Autowired
@Qualifier("jdbcOrderRepository")
private OrderRepository orderRepository;
}
6. IOC 的优势与挑战
优势
- 解耦:减少类之间的直接依赖,提高模块化。
- 可维护性:配置与代码分离,便于修改和扩展。
- 可测试性:依赖可替换为 Mock 对象,方便单元测试。
- 统一管理:集中管理对象生命周期和依赖关系。
挑战
- 启动性能:大型应用初始化可能较慢。
- 配置复杂度:XML 配置可能冗长,注解需谨慎使用避免过度耦合。
- 循环依赖 :需通过构造器注入或
@Lazy
解决。
7. 实际应用示例
(1) 三层架构中的依赖注入
java
@Repository
public class JdbcUserRepository implements UserRepository {
// 数据访问实现
}
@Service
public class UserService {
private final UserRepository userRepository;
@Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
@RestController
public class UserController {
@Autowired
private UserService userService;
}
(2) 条件化 Bean 配置
java
@Configuration
public class AppConfig {
@Bean
@ConditionalOnProperty(name = "cache.enabled", havingValue = "true")
public CacheManager cacheManager() {
return new RedisCacheManager();
}
}
