理解 IoC 容器的源码,不是为了背诵实现细节,而是为了在遇到诡异的 Bean 加载失败、循环依赖报错、@PostConstruct 未执行等问题时,能有一套清晰的分析框架。Spring IoC 容器的主线流程可以归纳为 容器启动 → BeanDefinition 加载与注册 → Bean 实例化与依赖注入 → 初始化 四个阶段。下面以 AnnotationConfigApplicationContext 为入口,展示最常用场景下的核心链路。
26.2.1 容器启动与配置类注册
AnnotationConfigApplicationContext 的构造器主要完成三件事:创建内部的 DefaultListableBeanFactory、注册配置类本身的 BeanDefinition、以及调用 refresh() 启动容器。
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
this(); // 1. 实例化 DefaultListableBeanFactory
register(componentClasses); // 2. 将配置类注册为 BeanDefinition
refresh(); // 3. 容器刷新入口
}
refresh() 是 Spring 容器最核心的方法,所有 ApplicationContext 实现都会执行相同的刷新步骤,它定义了容器启动的完整生命周期:
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
prepareRefresh(); // 准备环境、校验属性
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
prepareBeanFactory(beanFactory); // 添加系统 BeanPostProcessor 等
postProcessBeanFactory(beanFactory);// 空方法,留给子类扩展
invokeBeanFactoryPostProcessors(beanFactory); // 处理 BeanFactoryPostProcessor
registerBeanPostProcessors(beanFactory); // 注册 BeanPostProcessor
initMessageSource(); // 国际化
initApplicationEventMulticaster(); // 事件广播器
onRefresh(); // 子类扩展(如启动 WebServer)
registerListeners(); // 注册事件监听器
finishBeanFactoryInitialization(beanFactory); // 初始化所有剩余的单例 Bean
finishRefresh(); // 清理缓存、发布 ContextRefreshedEvent
}
}
篇幅所限,这里不逐一展开每个步骤,重点关注 BeanDefinition 的加载和 Bean 的实例化过程。
26.2.2 BeanDefinition 的加载与注册
invokeBeanFactoryPostProcessors 步骤会调用所有注册的 BeanFactoryPostProcessor,其中最关键的当属 ConfigurationClassPostProcessor。它负责处理 @Configuration 类,通过 ConfigurationClassParser 解析 @ComponentScan、@Import、@Bean 等方法,最终将扫描到的组件转化为 BeanDefinition 并注册到 DefaultListableBeanFactory 的 beanDefinitionMap 中。
核心链路简化为:
ConfigurationClassPostProcessor.processConfigBeanDefinitions()- 遍历所有配置类,调用
ConfigurationClassParser.parse() - 解析
@ComponentScan,扫描指定包路径,将@Component等注解标注的类包装为ScannedGenericBeanDefinition - 解析
@Import和@Bean方法注册额外的 BeanDefinition - 调用
DefaultListableBeanFactory.registerBeanDefinition()将 BeanDefinition 存入beanDefinitionMap(ConcurrentHashMap),同时更新beanDefinitionNames列表。
此时,容器包含了所有要管理的 Bean 的定义信息,但还没有创建任何实例(除了部分 BeanFactoryPostProcessor 和 BeanPostProcessor)。
26.2.3 单例 Bean 的初始化主线
finishBeanFactoryInitialization(beanFactory) 负责实例化所有非懒加载的单例 Bean。内部调用 beanFactory.preInstantiateSingletons(),遍历 beanDefinitionNames,对每个非抽象的 Bean 调用 getBean(beanName)。
getBean 是 Bean 获取的统一入口,它委托给 doGetBean(),并经历以下关键步骤:
1. 尝试从缓存获取
首先检查 getSingleton(beanName),如果对象已经在单例池(singletonObjects,一级缓存)中存在,直接返回。这保证了单例的复用性。
2. 处理循环依赖的早期暴露
若缓存中不存在,开始创建新的单例。调用 getSingleton(String beanName, ObjectFactory<?> singletonFactory),该方法会将当前 Bean 名放入“正在创建”标记(singletonsCurrentlyInCreation)中,然后回调 singletonFactory 的 createBean 方法。
3. 实例化 Bean(createBeanInstance)AbstractAutowireCapableBeanFactory.createBeanInstance() 负责创建原始对象。它会:
- 通过
resolveBeanClass加载 Bean 类 - 判断是否有
Supplier、@Lookup方法或工厂方法,若无,则选择合适的构造器进行实例化(autowireConstructor或instantiateBean)。实例化通常使用反射或 CGLIB 动态子类化,最终产生一个 Bean 实例,但此时属性尚未填充。
4. 允许后处理器提前处理
实例化后,调用 applyMergedBeanDefinitionPostProcessors,将合并后的 BeanDefinition 交给 MergedBeanDefinitionPostProcessor 处理,例如 AutowiredAnnotationBeanPostProcessor 收集 @Autowired 注入点。
5. 提前暴露半成品解决循环依赖
如果当前 Bean 是单例、允许循环引用且正在创建中,Spring 会调用 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean))。这里会将一个能产生早期引用的 ObjectFactory 放入三级缓存(singletonFactories)。当 B 在创建过程中需要注入 A 时,可以从三级缓存获取 A 的早期引用(如果有代理,此处会提前生成代理对象),从而打破循环。
6. 属性填充(populateBean)populateBean(beanName, mbd, instanceWrapper) 完成依赖注入。它会遍历所有 InstantiationAwareBeanPostProcessor,调用 postProcessProperties 来处理 @Autowired、@Value、@Resource 等注解的注入。例如 AutowiredAnnotationBeanPostProcessor 通过反射找到标记的字段或方法,从容器中解析并注入依赖。
7. 初始化(initializeBean)
属性注入完成后,执行初始化回调:
- 调用
invokeAwareMethods:如果 Bean 实现了BeanNameAware、BeanClassLoaderAware、BeanFactoryAware,注入相应资源。 - 调用所有
BeanPostProcessor.postProcessBeforeInitialization:其中ApplicationContextAwareProcessor会在此处注入 ApplicationContext 等 Aware。 - 调用
invokeInitMethods:先检查是否实现InitializingBean,调用afterPropertiesSet;再调用用户自定义的init-method或@PostConstruct标注的方法(注意:@PostConstruct实际由CommonAnnotationBeanPostProcessor在postProcessBeforeInitialization中优先执行)。 - 调用所有
BeanPostProcessor.postProcessAfterInitialization:这里可能生成 AOP 代理对象,例如AbstractAutoProxyCreator会判断是否需要创建代理,如果需要则返回代理对象,替换原始 Bean。
8. 放入单例池
经过初始化后的最终 Bean(可能是代理)被放入一级缓存 singletonObjects,并从二级、三级缓存中移除,完成创建。
26.2.4 三级缓存解决循环依赖的核心机制
这是面试和实际问题的高频点,有必要单独说明。Spring 使用三级缓存:
- singletonObjects(一级):完全初始化好的单例池。
- earlySingletonObjects(二级):存放早期暴露的未完全初始化的 Bean(已从三级移入)。
- singletonFactories(三级):存放可生成 Bean 早期引用的 ObjectFactory。
当 A 与 B 循环依赖时,流程如下:
- 创建 A,实例化后将 ObjectFactory 放入三级缓存,然后填充属性时发现需要 B。
- 尝试获取 B,B 不存在,开始创建 B。
- B 实例化后,填充属性时需要 A,去缓存查找 A。
- 在三级缓存找到 A 的 ObjectFactory,调用
getObject()(可能触发getEarlyBeanReference产生 A 的代理),将结果放入二级缓存,并从三级缓存移除。 - B 获得 A 的早期引用,完成属性填充和初始化,成为完整的单例放入一级缓存。
- 回到 A 的创建,A 继续完成属性填充(此时 B 已完整),初始化,最终放入一级。
三级缓存的关键在于 提前暴露一个对象引用,即便该对象尚未完全初始化。这只能解决单例的 Setter 注入循环依赖,无法解决构造器注入的循环依赖(因为构造器触发时就要求立刻获得依赖,根本来不及暴露)。
26.2.5 容器优雅关闭
当应用关闭时(如 context.close()),容器调用 doClose(),最终会回调每个单例 Bean 的销毁方法:先执行 DisposableBean.destroy(),再调用自定义的 destroy-method 或 @PreDestroy 方法。CommonAnnotationBeanPostProcessor 负责 @PreDestroy 的调度。整个过程确保外部资源(连接池、线程池)被安全释放。
理解了这一主干流程,再遇到 NoSuchBeanDefinitionException、循环依赖错误、@PostConstruct 失效等问题时,你就能够快速定位是 BeanDefinition 未注册、构造器参数解析失败,还是后处理器未正确配置,从而对症下药。下一节我们将深入 ApplicationContext 与 BeanFactory 的区别,进一步夯实容器基础。