Spring IOC(控制反转)底层原理详解
Spring IOC(控制反转)底层原理详解
Spring 的核心是 IoC(Inversion of Control,控制反转)容器,它将对象的创建、装配、管理交给 Spring 框架,从而降低组件之间的耦合度。理解其底层原理,需要从 IoC 思想、Bean 定义与注册、依赖注入机制、Bean 生命周期、循环依赖解决 等多个维度深入剖析。
一、IoC 与 DI 的本质
- 控制反转:传统程序中,对象由使用者主动
new创建,依赖关系硬编码在代码中。IoC 将控制权转移到容器,容器负责创建和管理对象,应用程序只需声明“我需要什么”。 - 依赖注入(DI):容器在创建对象时,动态地将它所依赖的其他对象通过构造器、Setter 或字段注入进去。这是 IoC 的具体实现方式。
Spring 的 IoC 容器通过 反射 和 工厂模式 实现对象的动态创建与装配。
二、Spring IoC 容器的核心组件
| 组件 | 作用 |
|---|---|
BeanDefinition |
存储 Bean 的元信息(类名、作用域、依赖、初始化方法等) |
BeanDefinitionRegistry |
注册表,保存所有 BeanDefinition |
BeanFactory |
最底层的 IoC 容器,提供 Bean 的获取、判断类型等基础功能 |
ApplicationContext |
继承 BeanFactory,增加国际化、事件传播、AOP 等企业级功能 |
BeanPostProcessor |
Bean 的后置处理器,允许在 Bean 初始化前后进行增强 |
InstantiationStrategy |
实例化策略,默认使用 CGLIB 或 JDK 反射创建对象 |
三、IoC 容器启动过程(核心流程)
以典型的 AnnotationConfigApplicationContext 为例,启动代码如下:
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
MyService service = ctx.getBean(MyService.class);
启动过程分为三个阶段:配置解析 → Bean 定义注册 → Bean 实例化与初始化。
1. 配置解析与 BeanDefinition 生成
- 根据传入的配置类(
@Configuration),Spring 使用ClassPathBeanDefinitionScanner扫描指定包下的@Component、@Service、@Repository等注解。 - 对于每个被标注的类,创建一个
BeanDefinition对象(通常为ScannedGenericBeanDefinition),记录:- Bean 的类全名
- 作用域(默认
singleton) - 构造器参数依赖
- 属性依赖(
@Autowired标记的字段/方法) - 初始化方法、销毁方法等
- 如果存在
@Bean方法,则生成ConfigurationClassBeanDefinition,存储工厂方法和参数信息。 - 生成的
BeanDefinition会通过BeanDefinitionRegistry注册到容器(本质是一个ConcurrentHashMap)。
2. Bean 实例化与初始化(核心步骤)
当调用 ctx.getBean() 或容器预加载单例 Bean(默认非懒加载)时,触发实例化过程:
步骤概览:
- 实例化对象(通过反射或 CGLIB 创建原始对象)
- 填充属性(执行依赖注入)
- BeanNameAware / BeanClassLoaderAware 回调
- BeanPostProcessor.postProcessBeforeInitialization
- @PostConstruct / afterPropertiesSet / init-method
- BeanPostProcessor.postProcessAfterInitialization
- 注册销毁回调(单例 Bean)
详细底层实现:
- 实例化:通过
InstantiationStrategy的策略模式,默认使用SimpleInstantiationStrategy,利用Constructor.newInstance()或 CGLIB 动态创建字节码对象(针对没有无参构造且被代理的场景)。 - 依赖注入:遍历
BeanDefinition中的依赖信息(如@Autowired字段),通过AutowiredAnnotationBeanPostProcessor进行解析:- 根据类型(
resolveDependency)查找匹配的 Bean(可能触发递归创建) - 通过反射
Field.set()或Method.invoke()注入
- 根据类型(
- Aware 回调:如果 Bean 实现了
BeanNameAware、BeanFactoryAware等接口,容器会调用对应方法传入自身引用。 - BeanPostProcessor:典型的应用如
ApplicationContextAwareProcessor注入ApplicationContext,AbstractAutoProxyCreator生成 AOP 代理。
3. 完成并存入缓存
- 初始化完成后,单例 Bean 会被放入 单例池(
DefaultSingletonBeanRegistry中的singletonObjects:ConcurrentHashMap<String, Object>) - 原型 Bean 不会缓存,每次获取都会重新创建
四、Bean 的完整生命周期(结合源码顺序)
1. 解析得到 BeanDefinition
2. 实例化(构造器)
3. 依赖注入(填充属性)
4. 设置 BeanName(BeanNameAware)
5. 设置 BeanFactory(BeanFactoryAware)
6. 设置 ApplicationContext(ApplicationContextAware)
7. BeanPostProcessor.beforeInitialization
8. 执行初始化方法(@PostConstruct、afterPropertiesSet、init-method)
9. BeanPostProcessor.afterInitialization
10. 注册销毁回调(DisposableBean、@PreDestroy、destroy-method)
11. Bean 就绪(使用中)
12. 容器关闭时调用销毁方法
Bean 生命周期(全流程)以单例非懒加载 Bean为例,结合 Spring 源码关键步骤:
-
实例化 Instantiation
createBeanInstance()通过构造器或工厂方法创建对象实例(此时属性未设置,但对象已存在)。
-
属性填充 Populate
populateBean()执行 DI,如上节所述。此时会解析循环依赖并提前暴露早期对象。
-
Aware 接口回调
- 如果 Bean 实现了
BeanNameAware、BeanClassLoaderAware、BeanFactoryAware,Spring 会调用对应方法。 - 但注意:这些回调发生在属性填充之后,初始化之前。
- 如果 Bean 实现了
-
BeanPostProcessor 前置处理
- 调用所有
BeanPostProcessor.postProcessBeforeInitialization()。 - 例如
ApplicationContextAwareProcessor会在这一步注入ApplicationContext。
- 调用所有
-
初始化 Initialization
- 如果 Bean 实现了
InitializingBean,调用afterPropertiesSet()。 - 如果配置了
init-method(或@PostConstruct),调用指定的初始化方法。 @PostConstruct由CommonAnnotationBeanPostProcessor处理,实际也是在postProcessBeforeInitialization中触发的。
- 如果 Bean 实现了
-
BeanPostProcessor 后置处理
- 调用
postProcessAfterInitialization(),这是 AOP 动态代理的“着床点”之一(如AbstractAutoProxyCreator在这里生成代理对象)。
- 调用
-
Bean 就绪:存入
singletonObjects一级缓存,可供应用程序使用。 -
销毁 Destruction(容器关闭时)
- 如果实现了
DisposableBean,调用destroy()。 - 如果配置了
destroy-method(或@PreDestroy),调用指定方法。
- 如果实现了
注意:作用域为 prototype 的 Bean 创建后容器不再管理其生命周期(不会调用销毁方法)。
五、循环依赖的解决原理(纯 Singleton + 字段注入)
场景示例:
@Component
public class A {
@Autowired private B b;
}
@Component
public class B {
@Autowired private A a;
}
Spring 解决方法:三级缓存(概述)
- 一级缓存
singletonObjects:已完全初始化好的单例 Bean - 二级缓存
earlySingletonObjects:提前暴露的原始对象(尚未填充属性) - 三级缓存
singletonFactories:ObjectFactory工厂,用于生成提前暴露的 Bean(可被 AOP 增强)
执行流程:
- 创建 A:实例化 A(无依赖),得到原始对象
aRaw,将aRaw包装成ObjectFactory放入三级缓存。 - 注入 B:发现 A 依赖 B,开始创建 B。
- 创建 B:实例化 B,得到
bRaw,放入三级缓存;发现 B 依赖 A,从一级 → 二级 → 三级查找。- 从三级缓存中拿到 A 的工厂,调用
getEarlyBeanReference()得到 A 的早期引用(如果 A 需要 AOP,此处生成代理对象),放入二级缓存,并删除三级缓存。
- 从三级缓存中拿到 A 的工厂,调用
- B 成功获得 A 的早期引用(可能为原始对象或代理对象),完成 B 的属性注入,接着初始化 B,最终 B 进入一级缓存。
- 回到 A:现在 B 已经创建完成,A 完成属性注入,再执行 A 的初始化,最终 A 也进入一级缓存。
关键点:只支持单例(
singleton)作用域的字段注入或 Setter 注入的循环依赖。构造器注入无法解决(因为实例化时就要求依赖存在)。
循环依赖的解决方案(详解)
Spring 通过 三级缓存 解决大部分单例 Bean 的循环依赖(仅限 Setter/字段注入,构造器注入无法解决)。
1. 三级缓存数据结构(定义在 DefaultSingletonBeanRegistry)
/** 一级缓存:已完成初始化的单例Bean */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** 二级缓存:早期暴露的Bean(尚未填充属性,但已实例化) */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
/** 三级缓存:单例工厂,用于生成早期暴露的Bean(可产生代理) */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
2. 解决流程举例(A 依赖 B,B 依赖 A,通过 Setter 注入)
- 开始创建 A:
getBean("a")→doGetBean()→ 检测singletonObjects无 → 标记 A 为“正在创建” → 调用createBean("a")。 - 实例化 A:
createBeanInstance(),得到 A 的原始对象(未填充属性)。 - 关键步骤:提前暴露 A 到三级缓存:
addSingletonFactory("a", () -> getEarlyBeanReference("a", mbd, bean));ObjectFactory中封装了getEarlyBeanReference方法,该方法会调用SmartInstantiationAwareBeanPostProcessor.getEarlyBeanReference(),用于处理代理(如 AOP)。 - 进入
populateBean("a"),发现 A 需要注入 B →resolveDependency()→getBean("b")。 - 创建 B:同样步骤,实例化 B → 提前暴露 B 到三级缓存 → 填充 B 的属性时发现需要注入 A。
getBean("a")再次被调用,此时一级缓存无 A,但 A 处于“正在创建”状态。
Spring 发现当前创建中集合里有 A,尝试从二级缓存或三级缓存中获取:getSingleton("a", true)→- 先查二级缓存
earlySingletonObjects(为空), - 再从三级缓存拿到
ObjectFactory,调用getObject()得到 A 的早期引用(可能被代理), - 将该早期引用放入二级缓存,并删除三级缓存中的工厂。
- 先查二级缓存
- B 拿到 A 的早期引用(尚未完成初始化),B 继续完成属性填充和初始化,完成后将 B 放入一级缓存。
- 回到 A 的
populateBean(),B 已经就绪,成功注入 → 继续执行 A 的初始化流程。 - A 初始化完成后,将 A 放入一级缓存,并删除二、三级缓存中的相关记录。
3. 为什么需要三级缓存?二级不够吗?
- 二级缓存可以存储早期暴露的半成品对象(解决循环依赖),但如果循环依赖涉及 AOP 代理,则必须借助三级缓存。
因为 AOP 代理通常是在BeanPostProcessor.postProcessAfterInitialization()中创建,而循环依赖发生时对象尚在初始化过程中,需要一个在创建代理对象之前就能获取到正确代理的机制。
三级缓存中的ObjectFactory调用getEarlyBeanReference(),该钩子允许 AOP 后处理器提前生成代理并暴露出来(而不是等到初始化完成)。如果没有三级缓存,就只能在二级缓存中提前放入原始对象,导致最终注入的可能是原始对象而非代理对象。
4. 无法解决的循环依赖场景
- 构造器注入:因为实例化阶段就需要依赖对方,无法提前暴露。(使用
@Lazy可打破循环,延迟依赖加载) prototype作用域:Spring 不缓存原型 Bean,每次请求都创建新实例,遇到循环依赖会直接抛出BeanCurrentlyInCreationException。@Async或某些创建代理的后处理器生效前:有时需要额外配置,但通常上述三级缓存已能处理普通 AOP。
5. 其他辅助机制
- 可以通过
@Lazy注解:在依赖的字段或构造参数上加上@Lazy,Spring 会注入一个代理对象,实际调用时再创建真实 Bean。 - 也可以通过
@PostConstruct+ApplicationContext.getBean()延迟获取。
六、AOP 与 IoC 的协作
当 Bean 被 AOP 增强(如 @Transactional、@Async)时,BeanPostProcessor(例如 AnnotationAwareAspectJAutoProxyCreator)会在 postProcessAfterInitialization 阶段判断是否匹配切点。如果匹配,则返回一个 代理对象(CGLIB 或 JDK 动态代理),并覆盖原来的原始对象。因此,最终放入 singletonObjects 缓存的是代理对象,对外暴露的也是代理对象。
七、FactoryBean 的特殊处理
实现 FactoryBean 接口的 Bean 本身是一个工厂,调用 getBean("&beanName") 返回工厂实例,调用 getBean("beanName") 返回 FactoryBean.getObject() 生成的对象。Spring 内部通过 FactoryBeanRegistrySupport 管理,避免重复调用 getObject()。
八、底层技术支撑
| 技术 | 用途 |
|---|---|
反射(java.lang.reflect) |
调用构造器、方法、设置字段 |
| CGLIB | 动态生成子类实现代理或实例化(如没有无参构造时) |
ConcurrentHashMap |
存储单例池、BeanDefinition 注册表等缓存 |
LinkedHashSet |
记录正在创建中的 Bean 名称(用于循环依赖检测) |
策略模式(InstantiationStrategy) |
隔离实例化细节 |
| 观察者模式 | ApplicationEvent 多播器 |
九、总结
一句话:Spring IoC 底层是基于
BeanDefinition元数据、三级缓存、反射与动态代理,通过BeanPostProcessor扩展点,实现对象的全生命周期管理和依赖注入的核心容器。
Spring IOC 底层建立在精密的 BeanDefinition 注册、BeanFactory 缓存、BeanPostProcessor 扩展点之上。循环依赖的关键在于三级缓存 + 提前暴露工厂,它能兼顾普通对象和代理对象的早期引用。理解这些原理对于性能调优、排查 Bean 创建失败异常以及自定义扩展都至关重要。
更多推荐


所有评论(0)