Skip to content

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 追到 doGetBeancreateBeandoCreateBean
  • 说清构造器选择、字段注入、初始化回调和代理生成分别在哪里发生;
  • 解释 singleton 三级缓存、FactoryBean 产品缓存和合并定义缓存不是一回事;
  • 解释为什么过早 getBean 会导致注入或 AOP 不完整;
  • 解释创建失败后 Spring 为什么必须清理缓存和依赖关系;
  • 说清容器关闭时销毁顺序和资源释放;
  • 用断点、日志和 JFR 定位启动慢、死锁、重复创建、代理缺失和关闭卡住;
  • 运行一个能证明实际回调顺序的 JDK 8 Demo。

一、先画出源码地图

mermaid
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注解配置容器,组合读取器和扫描器
BeanFactoryBean获取和依赖解析基本契约
DefaultListableBeanFactory常用完整BeanFactory,定义注册、类型查找和依赖解析
AbstractBeanFactorydoGetBean、scope、父工厂、FactoryBean处理等
AbstractAutowireCapableBeanFactorycreateBean、doCreateBean、注入和初始化
DefaultSingletonBeanRegistrysingleton缓存、创建状态、依赖关系和销毁
BeanDefinitionRegistryBeanDefinition注册表
PostProcessorRegistrationDelegateBFPP/BPP发现、排序与调用协调

源码类名很多,但主干是:ApplicationContext 组织生命周期,BeanFactory 真正保存定义和创建 Bean,SingletonBeanRegistry 管理 singleton 状态。

三、AnnotationConfigApplicationContext 构造时做了什么

执行:

java
new AnnotationConfigApplicationContext(AppConfig.class)

看起来是一行,通常包含:

  1. 创建 DefaultListableBeanFactory
  2. 创建 AnnotatedBeanDefinitionReader
  3. 注册注解配置基础处理器;
  4. 创建 ClassPathBeanDefinitionScanner
  5. 注册传入的配置类为 BeanDefinition;
  6. 调用 refresh()

注解配置基础处理器中最关键的包括:

  • ConfigurationClassPostProcessor:解析配置类、扫描、Import和Bean方法;
  • AutowiredAnnotationBeanPostProcessor:处理Autowired和Value等注入点;
  • CommonAnnotationBeanPostProcessor:处理Resource、PostConstruct等常用注解;
  • 事件监听方法处理器等基础设施。

因此这些能力不是 JVM 自动识别注解,而是容器预先注册了处理器。

四、refresh() 完整骨架

Spring 5 AbstractApplicationContext.refresh() 的经典主线如下:

text
prepareRefresh
obtainFreshBeanFactory
prepareBeanFactory
postProcessBeanFactory
invokeBeanFactoryPostProcessors
registerBeanPostProcessors
initMessageSource
initApplicationEventMulticaster
onRefresh
registerListeners
finishBeanFactoryInitialization
finishRefresh

外层还负责:

  • 启动时间与状态标记;
  • refresh/close 并发协调;
  • 捕获 BeansException;
  • 失败时销毁已经创建的 singleton;
  • 标记 refresh 取消;
  • finally 中清理反射和注解等公共缓存。
mermaid
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

经典顺序:

  1. 调用通过 API 直接传入的 BDRPP;
  2. 从容器查找实现 PriorityOrdered 的 BDRPP;
  3. 查找实现 Ordered 的 BDRPP;
  4. 循环查找其余 BDRPP,直到没有新增处理器;
  5. 调用所有 BDRPP 的 postProcessBeanFactory
  6. 调用直接传入的普通 BFPP;
  7. 按 PriorityOrdered、Ordered、其余三组调用容器中的 BFPP;
  8. 清理合并定义等元数据缓存。

为什么要循环发现 BDRPP?因为一个注册器可能注册另一个注册器,新增注册器也必须在定义阶段获得执行机会。

5.1 ConfigurationClassPostProcessor 做什么

它作为重要 BDRPP:

  • 找配置类候选;
  • 解析 @Configuration
  • 处理 @ComponentScan
  • 处理 @Import
  • 处理 ImportSelector、DeferredImportSelector;
  • 处理 ImportBeanDefinitionRegistrar
  • 解析 @Bean 方法;
  • 处理 PropertySource 等配置元数据;
  • 注册新 BeanDefinition;
  • 增强 full configuration class,保证跨 @Bean 方法调用遵守容器语义。
mermaid
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”,还会:

  1. 关联 ConversionService;
  2. 在缺少用户解析器时加入嵌入值解析器;
  3. 提前创建 LoadTimeWeaverAware Bean;
  4. 停止使用临时 ClassLoader;
  5. 冻结 BeanDefinition 配置;
  6. 调用 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)。核心过程:

  1. transformedBeanName 处理别名和 FactoryBean & 前缀;
  2. 从 singleton 缓存查实例,必要时允许早期引用;
  3. 命中后通过 getObjectForBeanInstance 判断返回 Bean 本身还是 FactoryBean 产品;
  4. 检测 prototype 当前线程创建循环;
  5. 当前工厂没有本地定义时委托父 BeanFactory;
  6. 非 type-check 调用标记 Bean 已创建;
  7. 取得并合并 RootBeanDefinition;
  8. 校验定义不是 abstract;
  9. 处理 dependsOn 并注册依赖关系;
  10. 根据 singleton、prototype 或自定义 scope 创建;
  11. 再次通过 getObjectForBeanInstance 处理 FactoryBean;
  12. 校验 requiredType,必要时使用 TypeConverter 转换;
  13. 返回最终对象。
mermaid
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 创建协调范围内:

  1. 再次检查一级缓存;
  2. 调用 beforeSingletonCreation 标记正在创建;
  3. 执行 singletonFactory.getObject(),通常进入 createBean
  4. 捕获并记录创建异常;
  5. finally 调用 afterSingletonCreation 清除创建标记;
  6. 成功后 addSingleton 放入一级缓存;
  7. 同时移除二级缓存和三级工厂。

这既防止同一容器成功产生多个同名 singleton,也用于检测不允许的创建循环。锁粒度、并发行为和实现细节随版本演进,业务代码不应依赖“构造器只会在某条固定线程执行”之外的内部偶然行为。

十三、createBean() 外层做什么

AbstractAutowireCapableBeanFactory.createBean() 常见步骤:

  1. 解析 Bean class;
  2. 克隆或准备 RootBeanDefinition;
  3. 校验 method override;
  4. 调用 resolveBeforeInstantiation
  5. 若 InstantiationAwareBeanPostProcessor 在实例化前直接返回代理,则短路常规创建;
  6. 否则进入 doCreateBean
  7. 包装异常,附加资源和 Bean 名上下文。

实例化前短路代理与“实例化后初始化阶段生成 AOP 代理”不是同一条路径。大多数普通 Spring AOP Bean 常在初始化后处理阶段生成代理,但自定义 InstantiationAwareBPP 可以提前替换对象。

十四、doCreateBean() 完整执行链

text
createBeanInstance
applyMergedBeanDefinitionPostProcessors
addSingletonFactory(满足早期暴露条件时)
populateBean
initializeBean
校验早期引用与最终包装对象一致性
registerDisposableBeanIfNecessary
返回exposedObject
mermaid
flowchart 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 加入工厂:

java
addSingletonFactory(beanName,
    () -> getEarlyBeanReference(beanName, mbd, bean));

B 注入 A 时:

  1. 一级缓存没有 A;
  2. A 正在创建,允许早期引用;
  3. 二级缓存没有;
  4. 从三级工厂调用 getEarlyBeanReference
  5. AutoProxyCreator 有机会返回早期代理;
  6. 早期引用放入二级缓存并移除三级工厂;
  7. B 注入该引用;
  8. A 初始化后检查最终 exposedObject 与早期引用是否一致。

三级缓存的关键价值不只是“晚一点创建对象”,而是给代理处理器一次按需产生一致早期代理的机会。

详细循环依赖边界见循环依赖与三级缓存全过程

十八、populateBean() 怎样完成字段和Setter注入

populateBean 常见过程:

  1. BeanWrapper 为空时处理无实例场景;
  2. InstantiationAwareBPP 的 postProcessAfterInstantiation 可拒绝继续属性填充;
  3. 处理 XML 时代的 AUTOWIRE_BY_NAME / AUTOWIRE_BY_TYPE;
  4. InstantiationAwareBPP 的 postProcessProperties 修改 PropertyValues;
  5. AutowiredAnnotationBeanPostProcessor 在此解析 DependencyDescriptor 并注入字段/方法;
  6. CommonAnnotationBeanPostProcessor 注入 Resource;
  7. 执行依赖检查;
  8. 通过 BeanWrapper 应用剩余 PropertyValues。
mermaid
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,大致经历:

  1. 设置当前注入点上下文;
  2. 处理快捷解析;
  3. 处理 @Value 建议值、占位符和SpEL;
  4. 按依赖类型查找候选;
  5. 应用 autowireCandidate、泛型和 Qualifier;
  6. 处理 Primary、Priority和依赖名称;
  7. 检查 required 与唯一性;
  8. 注册 dependentBean 关系;
  9. 返回实例并完成必要转换;
  10. finally 恢复旧注入点上下文。

完整候选优先级见@Autowired@Resource注入全过程

二十、initializeBean() 的准确顺序

常见顺序:

  1. invokeAwareMethods:BeanNameAware、BeanClassLoaderAware、BeanFactoryAware;
  2. applyBeanPostProcessorsBeforeInitialization
  3. ApplicationContextAwareProcessor 等在前置处理阶段调用其他 Aware;
  4. CommonAnnotationBeanPostProcessor 等触发 @PostConstruct
  5. invokeInitMethods 调用 InitializingBean.afterPropertiesSet;
  6. 调用自定义 init-method;
  7. applyBeanPostProcessorsAfterInitialization
  8. AutoProxyCreator 可能返回 AOP 代理。

某个 BeanPostProcessor 返回 null 时,当前前置或后置处理链会停止继续调用后续处理器,并使用前一结果;这不是“整个 Bean 变成 null”。自定义 BPP 必须理解此契约。

mermaid
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,常见路径:

  1. Bean 创建前判断是否需要提前替换;
  2. 循环依赖时 getEarlyBeanReference 可能创建早期代理;
  3. 正常初始化后 postProcessAfterInitialization 查找匹配 Advisor;
  4. 构造 ProxyFactory;
  5. 选择 JDK 或 CGLIB;
  6. 返回代理作为 exposedObject;
  7. 一级缓存最终保存代理,而非仅保存原始 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创建中名称循环检测与状态管理
factoryBeanObjectCacheFactoryBean产品缓存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() 与销毁全过程

关闭主线通常包括:

  1. 标记关闭并防止重复关闭;
  2. 发布 ContextClosedEvent;
  3. LifecycleProcessor 执行 onClose,停止生命周期 Bean;
  4. BeanFactory destroySingletons()
  5. 按依赖关系和注册顺序销毁 Bean;
  6. 调用 DestructionAwareBeanPostProcessor;
  7. 调用 @PreDestroy
  8. 调用 DisposableBean.destroy;
  9. 调用自定义 destroy-method;
  10. 关闭 BeanFactory;
  11. 执行 onClose 模板扩展;
  12. 恢复监听器等上下文状态。
mermaid
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 用事件列表证明定义处理器、实例处理器、初始化和销毁的实际顺序:

java
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 版本为准。

预期关键关系:

text
BeanFactoryPostProcessor
    早于 AuditService 构造
构造
    早于 BeanNameAware
BeanNameAware
    早于 BeforeInitialization
BeforeInitialization
    早于 afterPropertiesSet
afterPropertiesSet
    早于 AfterInitialization
AfterInitialization
    早于 afterSingletonsInstantiated
业务调用
    早于 destroy

二十八、源码断点学习路线

第一轮:只看 refresh 骨架

断点:

text
AbstractApplicationContext.refresh
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors
PostProcessorRegistrationDelegate.registerBeanPostProcessors
DefaultListableBeanFactory.preInstantiateSingletons
AbstractApplicationContext.finishRefresh

只记录阶段顺序,不进入每个 lambda 和集合工具类。

第二轮:固定一个业务 Bean

条件断点 beanName.equals("auditService")

text
AbstractBeanFactory.doGetBean
AbstractAutowireCapableBeanFactory.createBean
AbstractAutowireCapableBeanFactory.doCreateBean
AbstractAutowireCapableBeanFactory.createBeanInstance
AbstractAutowireCapableBeanFactory.populateBean
AbstractAutowireCapableBeanFactory.initializeBean
DefaultSingletonBeanRegistry.addSingleton

观察 mbdbeanWrapperexposedObject 和三个 singleton 缓存。

第三轮:观察注入

text
AutowiredAnnotationBeanPostProcessor.postProcessProperties
DefaultListableBeanFactory.resolveDependency
DefaultListableBeanFactory.doResolveDependency
DefaultListableBeanFactory.findAutowireCandidates

记录 DependencyDescriptor 的类型、required、generic、qualifier 和候选名称。

第四轮:观察代理

text
AbstractAutoProxyCreator.getEarlyBeanReference
AbstractAutoProxyCreator.postProcessAfterInitialization
AbstractAutoProxyCreator.wrapIfNecessary
ProxyFactory.getProxy

对比原始 target class、早期引用 class 和最终 Bean class。

二十九、商业启动案例:采集平台

医疗采集平台启动时可能注册:

  • 数据源采集适配器;
  • 清洗规则;
  • 标准编码转换器;
  • 数据脱敏器;
  • MQ生产者与消费者;
  • 定时任务执行器;
  • 指标采集器;
  • 数据库与Redis客户端。

正确设计:

  1. BDRPP/BFPP 只注册或修改定义,不调用远程系统;
  2. BPP 不依赖重量级业务 Service;
  3. Bean 构造器不做无超时网络 I/O;
  4. 核心凭据和连接在 readiness 前验证;
  5. 非核心低频能力可受控 Lazy,但要预热和监控;
  6. SmartInitializingSingleton 用于校验完整策略集合,不执行长任务;
  7. 消费者启动顺序确保数据库迁移、缓存和处理器已准备好;
  8. 关闭时先停止接收新任务,再等待在途任务,最后关闭客户端和线程池。

三十、启动慢生产排查 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 没有被代理排查

  1. 确认对象由 Spring 创建,不是手工 new;
  2. 打印运行时 class 和 AopUtils.isAopProxy
  3. 检查 AutoProxyCreator 是否注册;
  4. 检查 Bean 是否在全部 BPP 注册前被 BFPP/BPP 依赖而过早创建;
  5. 检查 Advisor 和 Pointcut 是否匹配 class/method;
  6. 检查 final/private/static 和 JDK接口限制;
  7. 检查是否拿到 FactoryBean 本身而非产品,或反之;
  8. 检查早期引用和最终代理是否一致;
  9. 检查自调用是否绕过代理;
  10. 用最小上下文测试固定代理契约。

三十二、创建异常排查

不要只读最外层 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.7Java 8兼容的最后一代Boot主线之一
Spring 6 / Boot 3Java 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可以在循环依赖真正需要时生成一致的早期代理,避免一方拿原始对象、另一方拿最终代理。

三十六、关联知识与掌握验收

掌握验收:

  • 能不看资料写出 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的版本边界。