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(默认非懒加载)时,触发实例化过程:

步骤概览:
  1. 实例化对象(通过反射或 CGLIB 创建原始对象)
  2. 填充属性(执行依赖注入)
  3. BeanNameAware / BeanClassLoaderAware 回调
  4. BeanPostProcessor.postProcessBeforeInitialization
  5. @PostConstruct / afterPropertiesSet / init-method
  6. BeanPostProcessor.postProcessAfterInitialization
  7. 注册销毁回调(单例 Bean)
详细底层实现:
  • 实例化:通过 InstantiationStrategy 的策略模式,默认使用 SimpleInstantiationStrategy,利用 Constructor.newInstance() 或 CGLIB 动态创建字节码对象(针对没有无参构造且被代理的场景)。
  • 依赖注入:遍历 BeanDefinition 中的依赖信息(如 @Autowired 字段),通过 AutowiredAnnotationBeanPostProcessor 进行解析:
    • 根据类型(resolveDependency)查找匹配的 Bean(可能触发递归创建)
    • 通过反射 Field.set()Method.invoke() 注入
  • Aware 回调:如果 Bean 实现了 BeanNameAwareBeanFactoryAware 等接口,容器会调用对应方法传入自身引用。
  • BeanPostProcessor:典型的应用如 ApplicationContextAwareProcessor 注入 ApplicationContextAbstractAutoProxyCreator 生成 AOP 代理。
3. 完成并存入缓存
  • 初始化完成后,单例 Bean 会被放入 单例池DefaultSingletonBeanRegistry 中的 singletonObjectsConcurrentHashMap<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 源码关键步骤:

  1. 实例化 Instantiation

    • createBeanInstance() 通过构造器或工厂方法创建对象实例(此时属性未设置,但对象已存在)。
  2. 属性填充 Populate

    • populateBean() 执行 DI,如上节所述。此时会解析循环依赖并提前暴露早期对象。
  3. Aware 接口回调

    • 如果 Bean 实现了 BeanNameAwareBeanClassLoaderAwareBeanFactoryAware,Spring 会调用对应方法。
    • 但注意:这些回调发生在属性填充之后,初始化之前。
  4. BeanPostProcessor 前置处理

    • 调用所有 BeanPostProcessor.postProcessBeforeInitialization()
    • 例如 ApplicationContextAwareProcessor 会在这一步注入 ApplicationContext
  5. 初始化 Initialization

    • 如果 Bean 实现了 InitializingBean,调用 afterPropertiesSet()
    • 如果配置了 init-method(或 @PostConstruct),调用指定的初始化方法。
    • @PostConstructCommonAnnotationBeanPostProcessor 处理,实际也是在 postProcessBeforeInitialization 中触发的。
  6. BeanPostProcessor 后置处理

    • 调用 postProcessAfterInitialization(),这是 AOP 动态代理的“着床点”之一(如 AbstractAutoProxyCreator 在这里生成代理对象)。
  7. Bean 就绪:存入 singletonObjects 一级缓存,可供应用程序使用。

  8. 销毁 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:提前暴露的原始对象(尚未填充属性)
  • 三级缓存 singletonFactoriesObjectFactory 工厂,用于生成提前暴露的 Bean(可被 AOP 增强)

执行流程

  1. 创建 A:实例化 A(无依赖),得到原始对象 aRaw,将 aRaw 包装成 ObjectFactory 放入三级缓存。
  2. 注入 B:发现 A 依赖 B,开始创建 B。
  3. 创建 B:实例化 B,得到 bRaw,放入三级缓存;发现 B 依赖 A,从一级 → 二级 → 三级查找。
    • 从三级缓存中拿到 A 的工厂,调用 getEarlyBeanReference() 得到 A 的早期引用(如果 A 需要 AOP,此处生成代理对象),放入二级缓存,并删除三级缓存。
  4. B 成功获得 A 的早期引用(可能为原始对象或代理对象),完成 B 的属性注入,接着初始化 B,最终 B 进入一级缓存。
  5. 回到 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 创建失败异常以及自定义扩展都至关重要。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐