Spring IOC 容器详解
一、什么是 IOC
1.1 控制反转的基本概念
IOC (Inversion of Control) 控制反转,是一种设计思想,将原本在程序中手动创建对象的控制权交由 Spring 框架来管理。
传统 Java 程序设计中,我们直接在对象内部通过 new 进行创建对象,是程序主动去创建依赖对象;而 IOC 是由专门的容器来负责对象的创建和维护,我们只需要直接使用即可。
简单来说,IOC 就是对象的创建控制权被反转了:
- 传统方式:我们主动通过 new 来创建实例,自己管理对象的创建
- IOC 方式:对象的创建交给了 IoC 容器,我们被动地接收 IoC 容器创建好的对象
1.2 IOC 能解决什么问题
IOC 主要解决了两个核心问题:
- 解耦:将对象和对象之间的相互依赖关系解耦,减少了模块之间的耦合度
- 统一管理:集中管理对象的创建和生命周期,避免重复创建或资源浪费
1.3 IOC 和 DI 的关系
DI (Dependency Injection) 依赖注入是实现 IOC 的一种重要手段:
- IOC是一种思想,是目标
- DI是这种思想的具体实现方式,是手段
简单理解:IOC 是设计思想,DI 是实现方式。
二、Spring IOC 容器
2.1 Spring 提供的两种容器类型
Spring 提供了两种实现 IOC 容器的方式:
1. BeanFactory(基础型 IOC 容器)
这是最基础、最简单的容器,仅提供了 DI 的基本功能:
- 简单的对象工厂,维护了 Bean 定义和相互依赖关系
- 懒加载:默认延迟实例化,只有当客户端请求 Bean 时才创建实例
- 提供了基本的生命周期管理和对象创建
// 使用BeanFactory
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("beans.xml"));
MyBean myBean = (MyBean) beanFactory.getBean("myBean");
2. ApplicationContext(企业级 IOC 容器)
是 BeanFactory 的子接口,增加了更多企业级功能:
- 预初始化:容器启动时就实例化所有单例 Bean
- 提供了完整的生命周期管理
- 支持国际化、资源访问、事件传播
- 支持 AOP 整合
// 使用ApplicationContext
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
MyBean myBean = context.getBean("myBean", MyBean.class);
2.2 常用的 ApplicationContext 实现
实现类 | 用途 |
---|---|
ClassPathXmlApplicationContext | 从类路径加载 XML 配置 |
FileSystemXmlApplicationContext | 从文件系统加载 XML 配置 |
AnnotationConfigApplicationContext | 基于 Java 配置类加载 |
WebApplicationContext | Web 应用专用的 AppContext |
2.3 容器的继承体系
Spring IOC 容器的核心接口关系:
BeanFactory (interface)
↑
ListableBeanFactory (interface)
↑
ApplicationContext (interface)
↑
ConfigurableApplicationContext (interface)
↑
具体实现类(ClassPathXmlApplicationContext等)
三、IOC 容器的初始化过程
Spring IOC 容器的初始化过程可分为三大步骤:
3.1 资源定位(Resource)
从各种来源(XML 文件、注解、Java 配置类等)加载配置信息。
// XML配置方式
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
// 注解方式
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
3.2 Bean 定义解析(BeanDefinition)
将用户定义的 Bean 配置信息解析成 Spring 内部的 BeanDefinition 数据结构。
// Bean定义包含的主要信息
class BeanDefinition {
String beanClassName; // Bean的完整类名
boolean lazyInit; // 是否懒加载
String[] dependsOn; // 依赖的Bean
ConstructorArgumentValues constructorArgumentValues; // 构造参数
MutablePropertyValues propertyValues; // 属性值
String scope; // 作用域
String factoryBeanName; // 工厂Bean名称
String factoryMethodName; // 工厂方法名称
// ...其他配置
}
3.3 Bean 定义注册
将解析好的 BeanDefinition 注册到容器,形成 Bean 定义注册表。
// 伪代码展示
Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
beanDefinitionMap.put(beanName, beanDefinition); // 注册Bean定义
3.4 Bean 初始化和依赖注入
当容器启动或请求 Bean 时,根据 BeanDefinition 创建 Bean 实例,并进行依赖注入。
四、Bean 的生命周期
Spring Bean 的完整生命周期非常丰富,主要阶段如下:
4.1 Bean 生命周期完整流程
- 实例化:创建 Bean 实例(调用构造函数)
- 属性赋值:设置属性(依赖注入)
- 初始化:初始化 Bean
- Aware 接口相关方法调用(如 BeanNameAware、BeanFactoryAware 等)
- BeanPostProcessor 的前置处理
- 调用初始化方法(InitializingBean 接口和自定义 init 方法)
- BeanPostProcessor 的后置处理
- 使用:Bean 可以被应用程序使用
- 销毁:当容器关闭时,调用销毁方法(DisposableBean 接口和自定义 destroy 方法)
4.2 生命周期扩展点
Spring 提供了多种扩展点让我们介入 Bean 的生命周期:
1. Bean 自身的方法:
- 实现
InitializingBean
接口:afterPropertiesSet()
方法 - 实现
DisposableBean
接口:destroy()
方法 - 自定义初始化/销毁方法(在 XML 或注解中配置)
public class MyBean implements InitializingBean, DisposableBean {
// 属性设置后调用
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("初始化操作...");
}
// 容器关闭前调用
@Override
public void destroy() throws Exception {
System.out.println("清理资源...");
}
// 自定义初始化方法
public void customInit() {
System.out.println("自定义初始化...");
}
// 自定义销毁方法
public void customDestroy() {
System.out.println("自定义销毁...");
}
}
2. 容器级的处理器:
BeanPostProcessor
:对 Bean 初始化前后进行处理InstantiationAwareBeanPostProcessor
:在实例化前后进行处理BeanFactoryPostProcessor
:对 BeanFactory 进行操作
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("初始化前处理:" + beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("初始化后处理:" + beanName);
return bean;
}
}
五、依赖注入的方式
Spring 支持多种依赖注入方式,常用的有三种:
5.1 构造器注入
通过构造函数传入依赖,最符合依赖不可变原则,是官方推荐的注入方式。
@Service
public class UserServiceImpl implements UserService {
private final UserRepository userRepository;
// 构造器注入
@Autowired
public UserServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
XML 配置方式:
<bean id="userService" class="com.example.UserServiceImpl">
<constructor-arg ref="userRepository"/>
</bean>
5.2 Setter 注入
通过 setter 方法传入依赖,更具有灵活性,可以随时替换依赖。
@Service
public class OrderServiceImpl implements OrderService {
private PaymentService paymentService;
// setter注入
@Autowired
public void setPaymentService(PaymentService paymentService) {
this.paymentService = paymentService;
}
}
XML 配置方式:
<bean id="orderService" class="com.example.OrderServiceImpl">
<property name="paymentService" ref="paymentService"/>
</bean>
5.3 字段注入
直接在字段上使用注解注入,最简洁但不推荐,因为不便于测试且隐藏依赖关系。
@Service
public class NotificationServiceImpl implements NotificationService {
// 字段注入
@Autowired
private EmailService emailService;
}
5.4 三种注入方式对比
注入方式 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
构造器注入 | 依赖不可变、强制依赖、便于测试 | 参数过多时代码臃肿 | 必须的依赖,核心业务组件 |
Setter 注入 | 灵活、可选依赖、可修改 | 不保证依赖完整性 | 可选的依赖,外部配置 |
字段注入 | 代码简洁 | 隐藏依赖、难以测试 | 简单场景,不推荐核心业务 |
六、自动装配
6.1 什么是自动装配
自动装配是 Spring 自动解析 Bean 之间依赖关系的过程,容器会自动查找、分析和注入依赖。
6.2 自动装配的方式
Spring 支持以下几种自动装配方式:
- byName:根据属性名称自动装配
- byType:根据属性类型自动装配
- constructor:类似于 byType,但应用于构造函数参数
- autodetect:先尝试 constructor,再尝试 byType
- no:默认值,不进行自动装配,通过显式配置注入
6.3 使用@Autowired 注解
@Autowired
注解是最常用的自动装配手段,默认按类型注入:
@Service
public class ProductServiceImpl implements ProductService {
// 字段注入
@Autowired
private ProductRepository productRepository;
// 构造器注入
private final CategoryService categoryService;
@Autowired
public ProductServiceImpl(CategoryService categoryService) {
this.categoryService = categoryService;
}
// setter注入
private InventoryService inventoryService;
@Autowired
public void setInventoryService(InventoryService inventoryService) {
this.inventoryService = inventoryService;
}
}
6.4 处理自动装配中的歧义性
当存在多个相同类型的 Bean 时,可能导致自动装配歧义:
@Autowired
private PaymentService paymentService; // 错误:存在多个PaymentService实现
解决方法:
-
@Primary:标记首选的 Bean
@Service @Primary public class AliPayServiceImpl implements PaymentService { }
-
@Qualifier:指定 Bean 的名称
@Autowired @Qualifier("aliPayService") private PaymentService paymentService;
-
@Resource:按名称注入(Java 标准注解)
@Resource(name = "aliPayService") private PaymentService paymentService;
七、Bean 的作用域
Spring 提供了多种 Bean 的作用域:
作用域 | 说明 | 使用场景 |
---|---|---|
singleton | 默认值,每个容器中只有一个 Bean 实例 | 大部分无状态的 Bean |
prototype | 每次请求 Bean 时创建一个新实例 | 有状态的 Bean |
request | 每个 HTTP 请求创建一个实例 | Web 应用中的请求处理 |
session | 每个 HTTP 会话创建一个实例 | 用户会话相关 Bean |
application | 每个 ServletContext 创建一个实例 | 应用级别的配置 |
websocket | 每个 WebSocket 创建一个实例 | WebSocket 应用 |
配置 Bean 的作用域
注解方式:
@Component
@Scope("prototype")
public class ShoppingCart { /*...*/ }
XML 方式:
<bean id="shoppingCart" class="com.example.ShoppingCart" scope="prototype"/>
八、Bean 的配置方式
Spring 支持多种 Bean 的配置方式:
8.1 XML 配置
最传统的方式,集中管理 Bean 定义。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userRepository" class="com.example.UserRepositoryImpl"/>
<bean id="userService" class="com.example.UserServiceImpl">
<constructor-arg ref="userRepository"/>
</bean>
</beans>
8.2 Java 配置
使用@Configuration
和@Bean
注解进行配置。
@Configuration
public class AppConfig {
@Bean
public UserRepository userRepository() {
return new UserRepositoryImpl();
}
@Bean
public UserService userService() {
return new UserServiceImpl(userRepository());
}
}
8.3 注解配置
使用组件扫描和注解自动检测和注册 Bean。
@SpringBootApplication // 包含@ComponentScan
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserRepository userRepository;
// ...
}
@Repository
public class UserRepositoryImpl implements UserRepository {
// ...
}
8.4 三种配置方式的对比
配置方式 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
XML 配置 | 集中管理,修改不需重新编译 | 繁琐,类型不安全 | 第三方组件配置 |
Java 配置 | 类型安全,重构友好 | 修改需重新编译 | 复杂配置,编程式配置 |
注解配置 | 简洁,和代码放一起 | 分散,不便整体查看 | 业务组件自动装配 |
九、IOC 源码原理解析
9.1 IOC 容器的核心组件
Spring IOC 容器实现依赖于几个核心组件:
- BeanDefinition:保存 Bean 定义的元信息
- BeanDefinitionRegistry:管理 Bean 定义的注册表
- BeanFactory:Bean 工厂,负责创建和获取 Bean 实例
- PostProcessor:各种处理器,用于扩展容器功能
9.2 IOC 容器初始化流程简化版
以ClassPathXmlApplicationContext
为例:
// 伪代码表示初始化流程
public class ClassPathXmlApplicationContext {
public ClassPathXmlApplicationContext(String configLocation) {
// 1. 资源定位:加载配置文件
Resource resource = new ClassPathResource(configLocation);
// 2. 创建BeanFactory
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 3. 创建XML读取器
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
// 4. 加载、解析Bean定义并注册
reader.loadBeanDefinitions(resource);
// 5. BeanFactoryPostProcessor处理
invokeBeanFactoryPostProcessors(beanFactory);
// 6. 注册BeanPostProcessor
registerBeanPostProcessors(beanFactory);
// 7. 初始化国际化资源、事件多播器等
initMessageSource();
initApplicationEventMulticaster();
// 8. 实例化所有非懒加载的单例Bean
finishBeanFactoryInitialization(beanFactory);
// 9. 发布容器刷新事件
finishRefresh();
}
}
9.3 Bean 的创建过程简化版
// 伪代码表示Bean的创建过程
public Object createBean(String beanName, BeanDefinition beanDefinition) {
try {
// 1. 实例化前处理
Object bean = resolveBeforeInstantiation(beanName, beanDefinition);
if (bean != null) {
return bean; // 如果前置处理返回了代理对象,直接使用
}
// 2. 创建Bean实例
Object instance = createBeanInstance(beanName, beanDefinition);
// 3. 属性赋值前处理
applyMergedBeanDefinitionPostProcessors(beanName, instance, beanDefinition);
// 4. 属性赋值(依赖注入)
populateBean(beanName, instance, beanDefinition);
// 5. 初始化Bean
instance = initializeBean(beanName, instance, beanDefinition);
// 6. 处理循环依赖
registerDisposableBeanIfNecessary(beanName, instance, beanDefinition);
return instance;
} catch (Exception e) {
throw new BeanCreationException(beanName, "Creation of bean failed", e);
}
}
十、常见问题与最佳实践
10.1 循环依赖问题
当两个或多个 Bean 相互依赖时会产生循环依赖:
@Service
public class ServiceA {
@Autowired
private ServiceB serviceB;
}
@Service
public class ServiceB {
@Autowired
private ServiceA serviceA;
}
Spring 解决方案:
- 对于单例 Bean 的 setter 循环依赖,Spring 使用三级缓存解决
- 对于原型 Bean 的循环依赖或构造器循环依赖,Spring 无法解决,会抛出异常
10.2 最佳实践
-
优先使用构造器注入:使依赖关系更明确,且可确保必要依赖完整性
@Service public class UserServiceImpl implements UserService { private final UserRepository userRepository; public UserServiceImpl(UserRepository userRepository) { this.userRepository = userRepository; } }
-
合理使用 Bean 的作用域:大多数 Bean 默认单例即可,有状态 Bean 使用 prototype
@Component @Scope("prototype") public class ShoppingCart { /*...*/ }
-
避免滥用组件扫描:控制扫描范围,避免意外注册不需要的 Bean
@ComponentScan(basePackages = {"com.example.service", "com.example.repository"})
-
减少 XML 配置:优先使用 Java 配置和注解
@Configuration public class AppConfig { @Bean public DataSource dataSource() { DruidDataSource dataSource = new DruidDataSource(); dataSource.setUrl("jdbc:mysql://localhost:3306/mydb"); // ... return dataSource; } }
-
使用@Qualifier 避免歧义:当有多个同类型 Bean 时指定明确的名称
@Autowired @Qualifier("mysqlDataSource") private DataSource dataSource;
十一、总结
Spring IOC 容器是 Spring 框架的核心,通过控制反转和依赖注入的设计思想,实现了对象的创建和依赖关系管理的控制权由程序代码转移到容器。它为 Java 应用程序提供了松耦合、可测试性和模块化的基础。
主要特点和优势:
- 松耦合设计:组件之间的依赖关系被容器管理,降低了组件间的耦合度
- 声明式编程:通过配置声明组件间的依赖关系,而非硬编码
- 集中管理:统一管理所有组件的生命周期
- 易于测试:可以轻松替换组件的实现,便于单元测试
- AOP 支持:与 Spring AOP 无缝集成,支持面向切面编程