Spring 容器启动与 Bean 创建源码执行链
阅读 Spring 源码最容易犯的错误,是记住几十个类名,却不知道当前对象处于“定义阶段、实例化阶段、属性填充阶段还是初始化阶段”。正确方法是固定入口、固定一个 Bean、沿真实调用栈观察状态变化。
本页以 Spring Framework 5.3 为主要源码基线,Demo 使用 JDK 8。Spring 6 / Boot 3 的 Java 17 与 Jakarta 边界会单独说明。内部实现类可能随版本调整,但容器为什么必须先处理定义、再注册实例处理器、最后创建普通 Bean 的因果关系是稳定的。
学习目标
学完本页,你应当能够:
- 画出
AbstractApplicationContext.refresh()完整阶段和失败回滚路径; - 解释
ConfigurationClassPostProcessor为什么属于定义阶段; - 解释 BDRPP、BFPP、BPP 的发现、排序和执行时机;
- 从
getBean追到doGetBean、createBean、doCreateBean; - 说清构造器选择、字段注入、初始化回调和代理生成分别在哪里发生;
- 解释 singleton 三级缓存、FactoryBean 产品缓存和合并定义缓存不是一回事;
- 解释为什么过早
getBean会导致注入或 AOP 不完整; - 解释创建失败后 Spring 为什么必须清理缓存和依赖关系;
- 说清容器关闭时销毁顺序和资源释放;
- 用断点、日志和 JFR 定位启动慢、死锁、重复创建、代理缺失和关闭卡住;
- 运行一个能证明实际回调顺序的 JDK 8 Demo。
一、先画出源码地图
flowchart TD
A["ApplicationContext.refresh"] --> B["BeanDefinition注册与修改"]
B --> C["注册BeanPostProcessor"]
C --> D["finishBeanFactoryInitialization"]
D --> E["preInstantiateSingletons"]
E --> F["getBean与doGetBean"]
F --> G["createBean与doCreateBean"]
G --> H["实例化"]
H --> I["提前暴露工厂"]
I --> J["populateBean依赖注入"]
J --> K["initializeBean初始化"]
K --> L["BeanPostProcessor生成代理"]
L --> M["完整singleton进入一级缓存"]
M --> N["finishRefresh发布完成事件"]阅读时始终问:
- 当前操作的是 BeanDefinition 还是 Bean 实例?
- 当前对象是不是完整 Bean?
- 当前引用是原始对象、早期引用还是最终代理?
- 当前缓存以 Bean 名、类型还是注入点为键?
- 异常发生后哪些状态需要撤销?
二、核心类层次与职责
| 类型 | 核心职责 |
|---|---|
ApplicationContext | 应用容器能力入口,组合BeanFactory、事件、资源、环境等 |
AbstractApplicationContext | 实现refresh和close骨架 |
AnnotationConfigApplicationContext | 注解配置容器,组合读取器和扫描器 |
BeanFactory | Bean获取和依赖解析基本契约 |
DefaultListableBeanFactory | 常用完整BeanFactory,定义注册、类型查找和依赖解析 |
AbstractBeanFactory | doGetBean、scope、父工厂、FactoryBean处理等 |
AbstractAutowireCapableBeanFactory | createBean、doCreateBean、注入和初始化 |
DefaultSingletonBeanRegistry | singleton缓存、创建状态、依赖关系和销毁 |
BeanDefinitionRegistry | BeanDefinition注册表 |
PostProcessorRegistrationDelegate | BFPP/BPP发现、排序与调用协调 |
源码类名很多,但主干是:ApplicationContext 组织生命周期,BeanFactory 真正保存定义和创建 Bean,SingletonBeanRegistry 管理 singleton 状态。
三、AnnotationConfigApplicationContext 构造时做了什么
执行:
new AnnotationConfigApplicationContext(AppConfig.class)看起来是一行,通常包含:
- 创建
DefaultListableBeanFactory; - 创建
AnnotatedBeanDefinitionReader; - 注册注解配置基础处理器;
- 创建
ClassPathBeanDefinitionScanner; - 注册传入的配置类为 BeanDefinition;
- 调用
refresh()。
注解配置基础处理器中最关键的包括:
ConfigurationClassPostProcessor:解析配置类、扫描、Import和Bean方法;AutowiredAnnotationBeanPostProcessor:处理Autowired和Value等注入点;CommonAnnotationBeanPostProcessor:处理Resource、PostConstruct等常用注解;- 事件监听方法处理器等基础设施。
因此这些能力不是 JVM 自动识别注解,而是容器预先注册了处理器。
四、refresh() 完整骨架
Spring 5 AbstractApplicationContext.refresh() 的经典主线如下:
prepareRefresh
obtainFreshBeanFactory
prepareBeanFactory
postProcessBeanFactory
invokeBeanFactoryPostProcessors
registerBeanPostProcessors
initMessageSource
initApplicationEventMulticaster
onRefresh
registerListeners
finishBeanFactoryInitialization
finishRefresh外层还负责:
- 启动时间与状态标记;
- refresh/close 并发协调;
- 捕获 BeansException;
- 失败时销毁已经创建的 singleton;
- 标记 refresh 取消;
- finally 中清理反射和注解等公共缓存。
flowchart TD
A["进入refresh并加启动关闭协调锁"] --> B["prepareRefresh"]
B --> C["obtainFreshBeanFactory"]
C --> D["prepareBeanFactory"]
D --> E["postProcessBeanFactory模板扩展"]
E --> F["invokeBeanFactoryPostProcessors"]
F --> G["registerBeanPostProcessors"]
G --> H["初始化消息、事件和监听器"]
H --> I["finishBeanFactoryInitialization"]
I --> J["finishRefresh"]
J --> K["容器Active"]
I -- "创建异常" --> L["destroyBeans"]
L --> M["cancelRefresh"]
M --> N["异常继续抛出"]4.1 prepareRefresh()
主要完成:
- 设置启动时间;
- 设置 active=true、closed=false;
- 初始化属性源钩子;
- 校验必须存在的环境属性;
- 准备早期 ApplicationListener;
- 创建 earlyApplicationEvents 集合。
必须属性校验放在前面,是为了在创建大量 Bean 前尽早失败。
4.2 obtainFreshBeanFactory()
它调用 refreshBeanFactory() 并返回 ConfigurableListableBeanFactory。不同 ApplicationContext 实现细节不同:
- 注解容器通常复用构造时已有的 BeanFactory;
- 可刷新的 XML 上下文可能关闭旧工厂、创建新工厂、加载定义;
- GenericApplicationContext 一般只允许 refresh 一次。
所以不能把某个上下文实现的细节当成所有容器的统一结论。
4.3 prepareBeanFactory()
此阶段为 BeanFactory 安装运行环境能力,例如:
- Bean ClassLoader;
- SpEL BeanExpressionResolver;
- ResourceEditorRegistrar;
- ApplicationContextAwareProcessor;
- 忽略部分 Aware 接口的普通自动注入;
- 注册可解析依赖,如 BeanFactory、ResourceLoader、ApplicationEventPublisher、ApplicationContext;
- ApplicationListenerDetector;
- LoadTimeWeaver 相关处理;
- environment、systemProperties、systemEnvironment singleton。
Aware 注入并非都走普通按类型候选。例如 ApplicationContextAware 常由专门 BPP 回调。
4.4 postProcessBeanFactory()
这是模板方法,允许具体 ApplicationContext 在标准准备后、执行用户 BFPP 前修改工厂。WebApplicationContext 可能在此加入 Web scope、ServletContext 等能力。
4.5 为什么定义处理必须在普通Bean创建前
如果还没解析 @Configuration、@Import 和 @Bean 就开始创建业务 Bean,容器看到的定义集合是不完整的;如果还没注册 Autowired/AOP 处理器就创建 Bean,实例将错过注入或代理。因此顺序不能随意交换。
五、BDRPP 和 BFPP 怎样发现与排序
invokeBeanFactoryPostProcessors() 不是简单遍历一个 List。要先处理能继续注册定义的 BeanDefinitionRegistryPostProcessor,再处理普通 BeanFactoryPostProcessor。
经典顺序:
- 调用通过 API 直接传入的 BDRPP;
- 从容器查找实现
PriorityOrdered的 BDRPP; - 查找实现
Ordered的 BDRPP; - 循环查找其余 BDRPP,直到没有新增处理器;
- 调用所有 BDRPP 的
postProcessBeanFactory; - 调用直接传入的普通 BFPP;
- 按 PriorityOrdered、Ordered、其余三组调用容器中的 BFPP;
- 清理合并定义等元数据缓存。
为什么要循环发现 BDRPP?因为一个注册器可能注册另一个注册器,新增注册器也必须在定义阶段获得执行机会。
5.1 ConfigurationClassPostProcessor 做什么
它作为重要 BDRPP:
- 找配置类候选;
- 解析
@Configuration; - 处理
@ComponentScan; - 处理
@Import; - 处理
ImportSelector、DeferredImportSelector; - 处理
ImportBeanDefinitionRegistrar; - 解析
@Bean方法; - 处理 PropertySource 等配置元数据;
- 注册新 BeanDefinition;
- 增强 full configuration class,保证跨
@Bean方法调用遵守容器语义。
flowchart TD
A["初始配置类BeanDefinition"] --> B["ConfigurationClassPostProcessor"]
B --> C["解析ComponentScan"]
C --> D["扫描并注册组件定义"]
B --> E["解析Import与Selector"]
E --> F["注册导入定义"]
B --> G["解析Bean方法"]
G --> H["注册工厂方法定义"]
H --> I{"发现新的配置候选?"}
I -- "是" --> B
I -- "否" --> J["完成定义阶段"]5.2 BFPP 中过早 getBean 的后果
BFPP 应修改 BeanDefinition 和 BeanFactory。如果它注入或主动获取普通业务 Bean,该 Bean 可能在全部 BeanPostProcessor 注册前创建,后果包括:
- Autowired/Resource/PostConstruct 处理不完整;
- AOP 自动代理没有机会介入;
- 日志出现 not eligible for getting processed by all BeanPostProcessors;
- Bean 被某个基础设施长期持有原始引用;
- 启动顺序变得难以推理。
如果 BFPP 需要配置数据,应读取 Environment、BeanDefinition 或轻量基础设施,避免依赖普通业务 Service。
六、BeanPostProcessor 怎样注册
registerBeanPostProcessors() 从 BeanFactory 按类型找 BPP 名称,然后分组:
PriorityOrdered;Ordered;- 无顺序接口;
MergedBeanDefinitionPostProcessor通常在后面重新注册以调整内部顺序;- 最后重新注册 ApplicationListenerDetector。
注册期间会临时加入 BeanPostProcessorChecker。当某个 Bean 在全部 BPP 注册完成前创建,Checker 可输出“不适合被所有 BPP 处理”的提示。
为什么 BPP 本身可能不能被所有 BPP 处理
为了创建 BPP,容器必须先实例化 BPP 及其依赖。此时 BPP 链尚未完整,因此 BPP 和它过早依赖的对象可能无法享受后续处理器。基础设施 Bean 应尽量减少业务依赖。
七、事件和监听器为什么分早期与正常阶段
在事件广播器初始化前发布的事件会先进入 earlyApplicationEvents;广播器和监听器注册完成后再统一广播。这样启动早期事件不会因为 multicaster 尚未建立而直接丢失。
registerListeners() 同时处理:
- 静态注册的 ApplicationListener;
- 容器中的 Listener Bean 名称;
- 启动早期暂存事件。
Listener Bean 可延迟实例化,事件发生时再从 BeanFactory 获取。监听器中执行阻塞远程调用会拖慢发布线程;默认事件广播是否同步要根据实际 multicaster 配置判断。
八、finishBeanFactoryInitialization() 做什么
它不仅是“创建 singleton”,还会:
- 关联 ConversionService;
- 在缺少用户解析器时加入嵌入值解析器;
- 提前创建 LoadTimeWeaverAware Bean;
- 停止使用临时 ClassLoader;
- 冻结 BeanDefinition 配置;
- 调用
preInstantiateSingletons()。
冻结配置表示常规定义注册阶段已经结束,容器可更积极缓存合并元数据。它不是让 Java 对象变成不可变,也不代表运行期绝对无法动态注册;动态修改会增加缓存失效和并发复杂度,不应作为普通业务手段。
九、preInstantiateSingletons() 全过程
DefaultListableBeanFactory 复制 Bean 名列表后遍历:
- 跳过 abstract;
- 跳过非 singleton;
- 跳过 lazy-init;
- FactoryBean 可能先创建工厂本身,再根据 SmartFactoryBean 是否 eager 决定产品;
- 普通 Bean 调用
getBean(beanName)。
遍历结束后,再遍历已经创建的 singleton;实现 SmartInitializingSingleton 的 Bean 会收到 afterSingletonsInstantiated()。
这个回调适合“所有常规非懒 singleton 都已经创建后”的最终检查,例如校验策略映射重复、预建缓存索引。它不保证 lazy Bean、prototype 或未来动态 Bean 已经创建。
十、getBean() 与 doGetBean() 主线
getBean 多个重载最终进入 doGetBean(name, requiredType, args, typeCheckOnly)。核心过程:
transformedBeanName处理别名和 FactoryBean&前缀;- 从 singleton 缓存查实例,必要时允许早期引用;
- 命中后通过
getObjectForBeanInstance判断返回 Bean 本身还是 FactoryBean 产品; - 检测 prototype 当前线程创建循环;
- 当前工厂没有本地定义时委托父 BeanFactory;
- 非 type-check 调用标记 Bean 已创建;
- 取得并合并 RootBeanDefinition;
- 校验定义不是 abstract;
- 处理
dependsOn并注册依赖关系; - 根据 singleton、prototype 或自定义 scope 创建;
- 再次通过
getObjectForBeanInstance处理 FactoryBean; - 校验 requiredType,必要时使用 TypeConverter 转换;
- 返回最终对象。
flowchart TD
A["doGetBean"] --> B["转换Bean名称与FactoryBean前缀"]
B --> C["查询singleton与早期缓存"]
C --> D{"缓存命中?"}
D -- "是" --> E["getObjectForBeanInstance"]
D -- "否" --> F["检查父工厂与循环创建"]
F --> G["合并并校验BeanDefinition"]
G --> H["先创建dependsOn依赖"]
H --> I{"scope类型"}
I -- "singleton" --> J["getSingleton回调createBean"]
I -- "prototype" --> K["beforePrototypeCreation后createBean"]
I -- "自定义scope" --> L["Scope.get回调createBean"]
J --> M["处理FactoryBean产品"]
K --> M
L --> M
M --> N["类型校验并返回"]10.1 为什么先查缓存
singleton 常被频繁获取。先查缓存可避免重复解析定义和创建。循环依赖场景中,创建中的 Bean 还可能从二级或三级缓存得到早期引用。
10.2 dependsOn 不是普通注入
dependsOn 表示创建顺序和销毁依赖关系,不会自动把依赖对象赋值到字段。A dependsOn B 时先创建 B,销毁时通常先销毁依赖者 A,再销毁 B。
10.3 父子容器查找
当前 BeanFactory 没有本地定义时才委托父工厂。本地同名 Bean 会遮蔽父容器定义。按名称、按类型和层次化查找 API 的行为不同,排查时必须确认调用的是哪种 API。
十一、FactoryBean 名称与产品缓存
假设 Bean 名为 clientFactory:
getBean("clientFactory")通常返回FactoryBean.getObject()的产品;getBean("&clientFactory")返回 FactoryBean 实例;- 别名和
&会由名称转换逻辑处理; - singleton FactoryBean 的产品可缓存在
factoryBeanObjectCache; - FactoryBean 实例本身仍在普通 singleton 缓存中。
因此至少有两层对象:工厂 Bean 与产品对象。排查类型查询、AOP、销毁和缓存时不能混为一个实例。
十二、getSingleton 如何保证单例创建
带 ObjectFactory 的 getSingleton(beanName, singletonFactory) 会在 singleton 创建协调范围内:
- 再次检查一级缓存;
- 调用
beforeSingletonCreation标记正在创建; - 执行
singletonFactory.getObject(),通常进入createBean; - 捕获并记录创建异常;
- finally 调用
afterSingletonCreation清除创建标记; - 成功后
addSingleton放入一级缓存; - 同时移除二级缓存和三级工厂。
这既防止同一容器成功产生多个同名 singleton,也用于检测不允许的创建循环。锁粒度、并发行为和实现细节随版本演进,业务代码不应依赖“构造器只会在某条固定线程执行”之外的内部偶然行为。
十三、createBean() 外层做什么
AbstractAutowireCapableBeanFactory.createBean() 常见步骤:
- 解析 Bean class;
- 克隆或准备 RootBeanDefinition;
- 校验 method override;
- 调用
resolveBeforeInstantiation; - 若 InstantiationAwareBeanPostProcessor 在实例化前直接返回代理,则短路常规创建;
- 否则进入
doCreateBean; - 包装异常,附加资源和 Bean 名上下文。
实例化前短路代理与“实例化后初始化阶段生成 AOP 代理”不是同一条路径。大多数普通 Spring AOP Bean 常在初始化后处理阶段生成代理,但自定义 InstantiationAwareBPP 可以提前替换对象。
十四、doCreateBean() 完整执行链
createBeanInstance
applyMergedBeanDefinitionPostProcessors
addSingletonFactory(满足早期暴露条件时)
populateBean
initializeBean
校验早期引用与最终包装对象一致性
registerDisposableBeanIfNecessary
返回exposedObjectflowchart TD
A["doCreateBean"] --> B["createBeanInstance实例化"]
B --> C["MergedBeanDefinitionPostProcessor"]
C --> D{"允许singleton早期暴露?"}
D -- "是" --> E["三级缓存加入ObjectFactory"]
D -- "否" --> F["populateBean"]
E --> F
F --> G["initializeBean"]
G --> H["获得原始对象或代理exposedObject"]
H --> I["检查早期引用一致性"]
I --> J["注册销毁回调"]
J --> K["返回最终Bean"]注意:加入三级缓存发生在实例化之后、属性填充之前。构造器循环依赖在实例产生前就互相需要,因此没有对象可提前暴露。
十五、Bean 实例到底怎样创建
createBeanInstance 可能选择:
- InstanceSupplier;
- 工厂方法;
- 已缓存的构造器解析结果;
determineCandidateConstructors给出的候选构造器;- 按构造器自动注入;
- 默认无参构造器。
构造器选择不是简单的“参数最多”。框架要结合:
@Autowired(required=...);- 构造参数值;
- 参数类型和名称;
- 候选 Bean;
- 可选依赖;
- 类型转换;
- 是否存在唯一构造器;
- 已缓存的 resolvedConstructorOrFactoryMethod。
实例化策略最终可能使用反射或 CGLIB 处理 method injection。@Lookup 等方法覆盖需要生成子类,不等价于直接 Constructor.newInstance()。
十六、MergedBeanDefinitionPostProcessor 为什么在实例化后调用
父子 BeanDefinition 合并后得到 RootBeanDefinition。MergedBeanDefinitionPostProcessor 可根据最终 class 和合并定义缓存元数据,例如:
- AutowiredAnnotationBeanPostProcessor 查找并缓存注入字段、方法;
- CommonAnnotationBeanPostProcessor 查找 Resource、PostConstruct、PreDestroy;
- 其他处理器准备生命周期和代理元数据。
元数据缓存避免每次创建 prototype 都重复扫描全部字段和方法。类热替换、定义重置或缓存清理时必须让这些元数据失效,否则可能使用旧结构。
十七、三级缓存与早期引用全过程
DefaultSingletonBeanRegistry 的三类核心结构:
| 缓存 | 内容 |
|---|---|
singletonObjects | 已完成创建的完整singleton |
earlySingletonObjects | 已经从工厂取得的早期引用 |
singletonFactories | 能按需生成早期引用的ObjectFactory |
创建 A 后、注入前,Spring 加入工厂:
addSingletonFactory(beanName,
() -> getEarlyBeanReference(beanName, mbd, bean));B 注入 A 时:
- 一级缓存没有 A;
- A 正在创建,允许早期引用;
- 二级缓存没有;
- 从三级工厂调用
getEarlyBeanReference; - AutoProxyCreator 有机会返回早期代理;
- 早期引用放入二级缓存并移除三级工厂;
- B 注入该引用;
- A 初始化后检查最终 exposedObject 与早期引用是否一致。
三级缓存的关键价值不只是“晚一点创建对象”,而是给代理处理器一次按需产生一致早期代理的机会。
详细循环依赖边界见循环依赖与三级缓存全过程。
十八、populateBean() 怎样完成字段和Setter注入
populateBean 常见过程:
- BeanWrapper 为空时处理无实例场景;
- InstantiationAwareBPP 的
postProcessAfterInstantiation可拒绝继续属性填充; - 处理 XML 时代的 AUTOWIRE_BY_NAME / AUTOWIRE_BY_TYPE;
- InstantiationAwareBPP 的
postProcessProperties修改 PropertyValues; - AutowiredAnnotationBeanPostProcessor 在此解析 DependencyDescriptor 并注入字段/方法;
- CommonAnnotationBeanPostProcessor 注入 Resource;
- 执行依赖检查;
- 通过 BeanWrapper 应用剩余 PropertyValues。
flowchart TD
A["populateBean"] --> B["postProcessAfterInstantiation"]
B --> C{"允许继续填充?"}
C -- "否" --> H["跳过属性注入"]
C -- "是" --> D["处理byName或byType"]
D --> E["postProcessProperties"]
E --> F["Autowired与Resource解析依赖"]
F --> G["BeanWrapper应用PropertyValues"]
G --> H["属性填充完成"]字段注入在实例化后发生,所以构造器中读取字段注入值仍为 null。构造器参数依赖则必须在实例产生前解析,走另一条构造解析链。
十九、依赖解析怎样选择候选
DefaultListableBeanFactory.resolveDependency() 会先处理特殊类型:
- Optional;
- ObjectFactory / ObjectProvider;
- JSR-330 Provider;
- Lazy 代理;
- Stream、数组、Collection、Map 等多值依赖。
普通单值依赖进入 doResolveDependency,大致经历:
- 设置当前注入点上下文;
- 处理快捷解析;
- 处理
@Value建议值、占位符和SpEL; - 按依赖类型查找候选;
- 应用 autowireCandidate、泛型和 Qualifier;
- 处理 Primary、Priority和依赖名称;
- 检查 required 与唯一性;
- 注册 dependentBean 关系;
- 返回实例并完成必要转换;
- finally 恢复旧注入点上下文。
完整候选优先级见@Autowired与@Resource注入全过程。
二十、initializeBean() 的准确顺序
常见顺序:
invokeAwareMethods:BeanNameAware、BeanClassLoaderAware、BeanFactoryAware;applyBeanPostProcessorsBeforeInitialization;- ApplicationContextAwareProcessor 等在前置处理阶段调用其他 Aware;
- CommonAnnotationBeanPostProcessor 等触发
@PostConstruct; invokeInitMethods调用 InitializingBean.afterPropertiesSet;- 调用自定义 init-method;
applyBeanPostProcessorsAfterInitialization;- AutoProxyCreator 可能返回 AOP 代理。
某个 BeanPostProcessor 返回 null 时,当前前置或后置处理链会停止继续调用后续处理器,并使用前一结果;这不是“整个 Bean 变成 null”。自定义 BPP 必须理解此契约。
flowchart TD
A["initializeBean"] --> B["BeanName与BeanFactory等Aware"]
B --> C["BPP BeforeInitialization"]
C --> D["PostConstruct等生命周期回调"]
D --> E["afterPropertiesSet"]
E --> F["自定义init-method"]
F --> G["BPP AfterInitialization"]
G --> H["返回原始对象或代理"]更细生命周期回调见Bean生命周期全过程。
二十一、AOP 代理怎样接入 Bean 创建
AutoProxyCreator 本质是 BeanPostProcessor,常见路径:
- Bean 创建前判断是否需要提前替换;
- 循环依赖时
getEarlyBeanReference可能创建早期代理; - 正常初始化后
postProcessAfterInitialization查找匹配 Advisor; - 构造 ProxyFactory;
- 选择 JDK 或 CGLIB;
- 返回代理作为 exposedObject;
- 一级缓存最终保存代理,而非仅保存原始 target。
若 B 已经注入 A 的早期代理,A 初始化结束又产生另一个不一致包装对象,容器需要检测 raw injection despite wrapping 等问题。Spring 不能允许一部分 Bean 永久持有原始 A、另一部分持有最终代理,否则事务和切面语义不一致。
完整 Advisor 和 MethodInterceptor 链见Spring AOP代理与拦截器链全过程。
二十二、创建失败为什么必须回滚状态
假设 A 创建中已经:
- 标记为 currentlyInCreation;
- 注册三级工厂;
- 记录 dependentBean;
- 创建部分依赖;
- 打开文件、线程或连接;
随后 @PostConstruct 抛异常。若不清理:
- 后续 getBean 可能错误命中半成品;
- 创建中标记永远残留,后续都报循环;
- 依赖关系和销毁顺序错误;
- 资源泄漏;
- 再次 refresh 或关闭行为不可预测。
Spring 会移除失败 singleton、早期缓存和创建标记,并传播包含 Bean 名、资源位置和 cause 的 BeanCreationException。已经创建的依赖是否保留取决于它们是否独立成功、容器整体是否继续启动;refresh 失败通常销毁本轮已创建 singleton。
业务 Bean 在构造或 init 中自己申请的外部资源,也必须在异常路径主动关闭,不能假设 Spring 能知道所有局部资源。
二十三、常见缓存不能混为一谈
| 缓存/状态 | 典型内容 | 目的 |
|---|---|---|
| singletonObjects | 完整singleton | 快速复用最终对象 |
| earlySingletonObjects | 早期引用 | 循环依赖期间复用同一早期对象 |
| singletonFactories | 早期引用工厂 | 按需产生可能的代理 |
| registeredSingletons | 已注册名称顺序 | 管理和销毁 |
| singletonsCurrentlyInCreation | 创建中名称 | 循环检测与状态管理 |
| factoryBeanObjectCache | FactoryBean产品 | 缓存singleton产品 |
| mergedBeanDefinitions | 合并RootBeanDefinition | 避免重复合并父子定义 |
| injectionMetadataCache | 字段/方法注入元数据 | 避免重复反射扫描 |
| candidateConstructorsCache | 候选构造器 | 避免重复解析构造器 |
面试中只说“Spring 有三级缓存”会掩盖大量其他缓存。三级缓存专指 singleton 早期引用链,不是整个容器只有三个 Map。
二十四、scope 创建有什么区别
- singleton:通过 SingletonBeanRegistry 协调和缓存;
- prototype:每次 getBean 创建,使用当前线程的 prototype 创建标记检测循环,不进入 singleton缓存;
- request/session:委托对应 Scope.get,以请求或会话为边界保存目标;
- 自定义 scope:必须定义获取、移除、销毁回调和上下文标识。
prototype 销毁通常不由容器自动完整管理,调用方需要释放资源。singleton 注入 prototype 时,若只在创建时解析一次,singleton 会长期持有那一个实例;每次需要新对象应使用 ObjectProvider 或作用域代理。
二十五、容器启动完成阶段
finishRefresh() 常完成:
- 清理资源缓存;
- 初始化 LifecycleProcessor;
- 调用 lifecycle processor 的 onRefresh;
- 发布 ContextRefreshedEvent;
- 某些环境注册运行期监控信息。
ContextRefreshedEvent 表示 refresh 主流程完成,不等于所有外部依赖永久健康,也不保证 lazy Bean 已创建。监听器中的阻塞逻辑会延长启动尾部;生产 readiness 应结合外部依赖和业务预热定义。
二十六、close() 与销毁全过程
关闭主线通常包括:
- 标记关闭并防止重复关闭;
- 发布 ContextClosedEvent;
- LifecycleProcessor 执行 onClose,停止生命周期 Bean;
- BeanFactory
destroySingletons(); - 按依赖关系和注册顺序销毁 Bean;
- 调用 DestructionAwareBeanPostProcessor;
- 调用
@PreDestroy; - 调用 DisposableBean.destroy;
- 调用自定义 destroy-method;
- 关闭 BeanFactory;
- 执行 onClose 模板扩展;
- 恢复监听器等上下文状态。
flowchart TD
A["ApplicationContext.close"] --> B["发布ContextClosedEvent"]
B --> C["停止Lifecycle Bean"]
C --> D["destroySingletons"]
D --> E["先销毁依赖者"]
E --> F["DestructionAwareBPP"]
F --> G["PreDestroy"]
G --> H["DisposableBean.destroy"]
H --> I["自定义destroy-method"]
I --> J["清理缓存并关闭BeanFactory"]服务关闭卡住常见原因是销毁回调无限等待线程池、远程请求无超时、锁顺序错误或非守护线程未退出。Kubernetes terminationGracePeriod 必须大于应用停止接流量、处理在途请求和资源关闭所需时间。
二十七、JDK 8 可运行生命周期 Demo
以下 Demo 用事件列表证明定义处理器、实例处理器、初始化和销毁的实际顺序:
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
public class SpringSourceDemo {
private static final List<String> EVENTS = new ArrayList<String>();
static class AuditService implements BeanNameAware,
InitializingBean, SmartInitializingSingleton, DisposableBean {
AuditService() {
EVENTS.add("3.constructor");
}
public void setBeanName(String name) {
EVENTS.add("4.beanNameAware:" + name);
}
public void afterPropertiesSet() {
EVENTS.add("6.afterPropertiesSet");
}
public void afterSingletonsInstantiated() {
EVENTS.add("8.afterSingletonsInstantiated");
}
public void destroy() {
EVENTS.add("11.destroy");
}
void run() {
EVENTS.add("10.business");
}
}
@Configuration
static class Config {
@Bean
static BeanFactoryPostProcessor definitionProcessor() {
return beanFactory -> EVENTS.add("1.beanFactoryPostProcessor");
}
@Bean
static BeanPostProcessor tracingProcessor() {
return new BeanPostProcessor() {
public Object postProcessBeforeInitialization(
Object bean, String name) throws BeansException {
if (bean instanceof AuditService) {
EVENTS.add("5.beforeInitialization");
}
return bean;
}
public Object postProcessAfterInitialization(
Object bean, String name) throws BeansException {
if (bean instanceof AuditService) {
EVENTS.add("7.afterInitialization");
}
return bean;
}
};
}
@Bean
AuditService auditService() {
EVENTS.add("2.factoryMethod");
return new AuditService();
}
}
public static void main(String[] args) {
EVENTS.add("0.beforeContext");
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(Config.class);
EVENTS.add("9.refreshReturned");
context.getBean(AuditService.class).run();
context.close();
System.out.println(EVENTS);
}
}这里故意使用 static @Bean 创建 BFPP/BPP,避免为了调用配置实例方法而过早实例化配置类。输出中数字用于帮助观察,实际严格顺序应以运行结果和当前 Spring 版本为准。
预期关键关系:
BeanFactoryPostProcessor
早于 AuditService 构造
构造
早于 BeanNameAware
BeanNameAware
早于 BeforeInitialization
BeforeInitialization
早于 afterPropertiesSet
afterPropertiesSet
早于 AfterInitialization
AfterInitialization
早于 afterSingletonsInstantiated
业务调用
早于 destroy二十八、源码断点学习路线
第一轮:只看 refresh 骨架
断点:
AbstractApplicationContext.refresh
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors
PostProcessorRegistrationDelegate.registerBeanPostProcessors
DefaultListableBeanFactory.preInstantiateSingletons
AbstractApplicationContext.finishRefresh只记录阶段顺序,不进入每个 lambda 和集合工具类。
第二轮:固定一个业务 Bean
条件断点 beanName.equals("auditService"):
AbstractBeanFactory.doGetBean
AbstractAutowireCapableBeanFactory.createBean
AbstractAutowireCapableBeanFactory.doCreateBean
AbstractAutowireCapableBeanFactory.createBeanInstance
AbstractAutowireCapableBeanFactory.populateBean
AbstractAutowireCapableBeanFactory.initializeBean
DefaultSingletonBeanRegistry.addSingleton观察 mbd、beanWrapper、exposedObject 和三个 singleton 缓存。
第三轮:观察注入
AutowiredAnnotationBeanPostProcessor.postProcessProperties
DefaultListableBeanFactory.resolveDependency
DefaultListableBeanFactory.doResolveDependency
DefaultListableBeanFactory.findAutowireCandidates记录 DependencyDescriptor 的类型、required、generic、qualifier 和候选名称。
第四轮:观察代理
AbstractAutoProxyCreator.getEarlyBeanReference
AbstractAutoProxyCreator.postProcessAfterInitialization
AbstractAutoProxyCreator.wrapIfNecessary
ProxyFactory.getProxy对比原始 target class、早期引用 class 和最终 Bean class。
二十九、商业启动案例:采集平台
医疗采集平台启动时可能注册:
- 数据源采集适配器;
- 清洗规则;
- 标准编码转换器;
- 数据脱敏器;
- MQ生产者与消费者;
- 定时任务执行器;
- 指标采集器;
- 数据库与Redis客户端。
正确设计:
- BDRPP/BFPP 只注册或修改定义,不调用远程系统;
- BPP 不依赖重量级业务 Service;
- Bean 构造器不做无超时网络 I/O;
- 核心凭据和连接在 readiness 前验证;
- 非核心低频能力可受控 Lazy,但要预热和监控;
- SmartInitializingSingleton 用于校验完整策略集合,不执行长任务;
- 消费者启动顺序确保数据库迁移、缓存和处理器已准备好;
- 关闭时先停止接收新任务,再等待在途任务,最后关闭客户端和线程池。
三十、启动慢生产排查 Runbook
30.1 先判断慢在哪个 refresh 阶段
记录:
- BFPP/配置类解析耗时;
- BPP 注册耗时;
finishBeanFactoryInitialization耗时;finishRefresh监听器耗时;- 具体 Bean 构造、注入和 init 耗时。
Spring Boot 可结合 ApplicationStartup/StartupStep;JDK 层使用 JFR、线程栈、类加载和 GC 日志。
30.2 invokeBeanFactoryPostProcessors 慢
检查超宽 ComponentScan、重复扫描、复杂 ImportSelector、classpath 扫描、配置元数据和自定义注册器。不要通过全局 Lazy 解决定义扫描慢,因为 Lazy 只影响实例创建。
30.3 finishBeanFactoryInitialization 慢
按 BeanName 定位构造器、@PostConstruct、afterPropertiesSet、init-method、FactoryBean.getObject、远程连接、锁和大文件加载。采集线程栈判断是在 CPU、I/O 还是锁等待。
30.4 启动卡死
连续采集至少三份线程栈,检查:
- singleton 创建锁;
- 构造器或 init 的互相等待;
- 线程池任务等待当前启动线程;
- 类初始化锁;
- DNS、数据库和HTTP无超时;
- 监听器同步调用。
不要只重启掩盖证据;保留线程栈、JFR、启动日志和部署变更。
三十一、Bean 没有被代理排查
- 确认对象由 Spring 创建,不是手工 new;
- 打印运行时 class 和
AopUtils.isAopProxy; - 检查 AutoProxyCreator 是否注册;
- 检查 Bean 是否在全部 BPP 注册前被 BFPP/BPP 依赖而过早创建;
- 检查 Advisor 和 Pointcut 是否匹配 class/method;
- 检查 final/private/static 和 JDK接口限制;
- 检查是否拿到 FactoryBean 本身而非产品,或反之;
- 检查早期引用和最终代理是否一致;
- 检查自调用是否绕过代理;
- 用最小上下文测试固定代理契约。
三十二、创建异常排查
不要只读最外层 UnsatisfiedDependencyException。沿 cause 链记录:
- 当前 Bean 名;
- 注入点;
- 依赖 Bean 名;
- 构造器或工厂方法;
- 最深异常;
- Profile/Condition;
- 是否正在创建;
- 是否存在早期引用;
- 实际运行时代理类型;
- Spring/JDK版本。
常见分类:
| 异常 | 优先检查 |
|---|---|
| NoSuchBeanDefinitionException | 扫描、条件、上下文、类型和Qualifier |
| NoUniqueBeanDefinitionException | 候选、Primary、Qualifier、泛型 |
| BeanCurrentlyInCreationException | 构造循环、早期调用、代理不一致 |
| BeanCreationException | 构造、注入、init和BPP最深cause |
| BeanDefinitionOverrideException | 重复名称和覆盖策略 |
| FactoryBeanNotInitializedException | 工厂产品获取时机与循环 |
三十三、常见源码误区
| 误区 | 为什么错 |
|---|---|
| refresh就是创建Bean | 前面还有环境、工厂、定义处理、BPP、事件等阶段 |
| BeanDefinition就是Bean | 前者是创建元数据,后者是运行实例 |
| createBean只调用构造器 | 还包含元数据处理、注入、初始化、代理和销毁注册 |
| 所有Aware都在同一方法直接调用 | 部分由invokeAwareMethods,部分由专门BPP处理 |
| AOP永远只在初始化后创建 | 常见如此,但循环依赖还可能走earlyBeanReference |
| Spring总共只有三级缓存 | 三级缓存只描述singleton早期引用主线 |
| BFPP可以随意注入Service | 会导致普通Bean在BPP完整前过早创建 |
| ContextRefreshedEvent代表所有功能健康 | lazy Bean和外部依赖可能尚未验证 |
| close会自动关闭所有局部资源 | Spring只能管理它知道的Bean销毁回调 |
三十四、版本边界
| 版本线 | 学习重点 |
|---|---|
| JDK 7 + Spring 4 | 理解经典容器主线,示例避免lambda和新API |
| JDK 8 + Spring 5.3 | 大量存量商业系统基线,本页源码与Demo主基线 |
| Spring Boot 2.7 | Java 8兼容的最后一代Boot主线之一 |
| Spring 6 / Boot 3 | Java 17基线、Jakarta迁移、内部实现持续演进 |
源码断点必须与项目实际依赖源码包一致。不同版本的方法细节和并发实现可能变化,面试应先讲稳定主线,再说明“具体以版本源码为准”。
三十五、面试标准回答
refresh() 的核心流程
先准备环境并取得BeanFactory,再安装容器基础能力;随后执行BDRPP/BFPP完成BeanDefinition注册和修改,注册全部BeanPostProcessor,然后初始化消息、事件和监听器;finishBeanFactoryInitialization预实例化非懒singleton,最后finishRefresh启动生命周期Bean并发布刷新完成事件。任一步创建失败会销毁已创建singleton并取消refresh。
getBean() 的核心流程
先转换别名和FactoryBean前缀,查singleton与早期缓存;未命中则处理父工厂、合并BeanDefinition、dependsOn和scope,singleton通过getSingleton回调createBean。创建完成后再判断返回FactoryBean本身还是产品,并做类型校验。
doCreateBean() 做了什么
先选择构造器、工厂方法或Supplier实例化,再让MergedBeanDefinitionPostProcessor准备注入和生命周期元数据;满足条件时加入早期引用工厂,然后populateBean完成依赖注入,initializeBean执行Aware、BPP前置、初始化和BPP后置代理,最后校验早期引用并注册销毁回调。
BFPP 和 BPP 为什么不能调换
BFPP处理BeanDefinition,可能继续注册定义;BPP处理未来创建的Bean实例。必须先完成定义集合,再注册实例处理器,最后创建普通Bean。若先创建Bean,它可能错过Autowired、PostConstruct或AOP处理。
Spring 为什么需要三级缓存
一级保存完整singleton,三级保存能生成早期引用的工厂,二级保存工厂已经产生的早期引用。三级工厂让AutoProxyCreator可以在循环依赖真正需要时生成一致的早期代理,避免一方拿原始对象、另一方拿最终代理。
三十六、关联知识与掌握验收
- Spring IoC容器与依赖注入全过程
- Bean生命周期全过程
@Autowired与@Resource注入全过程@Lazy懒加载与代理全过程- 循环依赖与三级缓存全过程
- Spring AOP代理与拦截器链全过程
- Spring事务全过程
- Spring核心扩展点
- Spring面试知识点
掌握验收:
- 能不看资料写出 refresh 主要阶段和失败回滚;
- 能解释 ConfigurationClassPostProcessor 为什么先于普通 Bean 创建;
- 能说明 BDRPP 循环发现、BFPP分组和BPP注册顺序;
- 能从 doGetBean 追到 doCreateBean 并说明每层职责;
- 能解释实例化、属性填充、初始化、代理和销毁注册的边界;
- 能比较 singleton三级缓存、FactoryBean产品缓存和定义元数据缓存;
- 能解释早期代理为何必须与最终暴露对象一致;
- 能运行Demo证明BFPP、BPP、Aware、init、SmartInitializingSingleton和destroy顺序;
- 能按阶段定位启动慢、创建异常、代理缺失和关闭卡住;
- 能说明JDK 8/Spring 5与Spring 6/Boot 3的版本边界。
