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 等企业级功能
BeanPostProcessorBean 的后置处理器,允许在 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 实现了 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 源码关键步骤:

  1. 实例化 Instantiation

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

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

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

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

    • 如果 Bean 实现了 InitializingBean,调用 afterPropertiesSet()。
    • 如果配置了 init-method(或 @PostConstruct),调用指定的初始化方法。
    • @PostConstruct 由 CommonAnnotationBeanPostProcessor 处理,实际也是在 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:提前暴露的原始对象(尚未填充属性)
  • 三级缓存 singletonFactories:ObjectFactory 工厂,用于生成提前暴露的 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编程工具,助力开发者即刻编程。

更多推荐