阅读 Spring 启动源码时,以 AbstractApplicationContext.refresh() 为主线,不要从包目录逐类翻译。它是一套模板流程:准备容器、加工定义、注册基础设施、创建单例、发布完成事件。
Table of contents
Open Table of contents
用最小上下文设置断点
MiniBootSample下载只注册一个配置类并读取一个 Bean:
mvn -Dtest=SpringSourceLabTest#minimalBootCreatesAndClosesAContext test
在 AnnotationConfigApplicationContext.refresh() 设置断点,记录线程名、BeanDefinition 数量和关键处理器数量。最小入口比完整 Boot 应用更容易看到因果关系。
refresh 的六个阶段
- 准备 Context 状态、属性源和启动时间;
- 获得并准备 BeanFactory;
- 执行
BeanFactoryPostProcessor,允许修改 BeanDefinition; - 注册
BeanPostProcessor、事件广播器等基础设施; finishBeanFactoryInitialization创建剩余非懒加载单例;- 发布 Context 已刷新事件。
如果中途失败,refresh 会销毁已创建单例并重置状态。启动异常不能只看最外层 BeanCreationException,要沿最深 cause 找到第一个业务配置错误。
模板方法为什么重要
AbstractApplicationContext 固定总流程,子类在加载 BeanDefinition、准备环境等位置提供差异。这就是模板方法模式:父类控制算法骨架,子类实现局部步骤。
源码阅读时把方法分为“主线阶段”和“阶段内部细节”。第一轮只写一页调用图;第二轮再针对配置解析、实例化或事件选择一条支线。
BeanFactory 扩展必须提前执行
BeanFactoryPostProcessor 修改的是配方,所以必须在普通单例创建前执行。BeanPostProcessor 加工的是对象,所以必须在目标 Bean 初始化前注册。顺序反了,早期 Bean 就会绕过代理和注解处理。
这也是“不应在后置处理器注册期间随意 getBean”的原因:提前创建会产生不完整生命周期。
下一步
阅读Bean 生命周期与后置处理器,沿单个对象追踪实例化、注入、初始化、代理和销毁。