# Springboot 中BeanDefinition是在什么阶段被创建成Bean的

这些 BeanDefinition 到底是哪一行代码、在哪个时间点,真正变成「看得见摸得着」的 Bean 实例,并塞进 BeanFactory 的?


1. 结论

Bean 的创建(初始化)发生在
AbstractApplicationContext#refresh() →
finishBeanFactoryInitialization(beanFactory) →
beanFactory.preInstantiateSingletons() →
AbstractBeanFactory#getBean(name) →
doGetBean(...) →
createBean(...) →
实例化、填充、初始化、代理,最终放进 DefaultSingletonBeanRegistry 的一级缓存(singletonObjects)。


2. 全景时序图

scss 复制代码
启动主类 main()
└─ SpringApplication.run()
   └─ new AnnotationConfigApplicationContext()
      └─ refresh()  // AbstractApplicationContext
         ├─ 1. prepareRefresh()
         ├─ 2. obtainFreshBeanFactory()          // 已有 BeanDefinition,但无实例
         ├─ 3. postProcessBeanFactory()
         ├─ 4. invokeBeanFactoryPostProcessors() // 解析 @ComponentScan、@Configuration
         ├─ 5. registerBeanPostProcessors()      // 注册后置处理器
         ├─ 6. initMessageSource()
         ├─ 7. initApplicationEventMulticaster()
         ├─ 8. onRefresh()
         ├─ 9. finishBeanFactoryInitialization() // ← 今天的主角
         │   └─ beanFactory.preInstantiateSingletons()
         │       └─ getBean(beanName)            // 首次触发创建
         │           └─ doCreateBean()
         │               ├─ createBeanInstance() // 反射 or CGLIB 构造
         │               ├─ populateBean()       // 依赖注入
         │               └─ initializeBean()     //  Aware、@PostConstruct、init-method、AOP 代理
         └─ 10. finishRefresh()                  // 事件发布,启动完成

3. 关键代码逐行定位

阶段 类 / 方法 行号(6.x 版本) 说明
入口 AbstractApplicationContext#finishBeanFactoryInitialization 913 行 显式转换 beanFactory → DefaultListableBeanFactory
循环 DefaultListableBeanFactory#preInstantiateSingletons 928 行 遍历所有 非抽象、单例、非懒加载 的 BeanDefinition
触发 AbstractBeanFactory#getBean(beanName) 211 行 第一次走 getBean,才会真正 create
创建 AbstractAutowireCapableBeanFactory#doCreateBean 594 行 实例化 + 属性赋值 + 初始化
缓存 DefaultSingletonBeanRegistry#addSingleton 153 行 创建完后放进 ConcurrentHashMap singletonObjects

懒加载(@Lazy(true))的 Bean 不会在这一步创建,而是第一次 getBean 时才触发。


4. 源码片段速读

4.1 只关心单例、非懒加载

java 复制代码
// DefaultListableBeanFactory#preInstantiateSingletons
for (String beanName : beanDefinitionNames) {
    RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
    if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
        getBean(beanName);   // 就是这一行触发创建
    }
}

4.2 真正的创建逻辑

java 复制代码
// AbstractAutowireCapableBeanFactory#doCreateBean
instanceWrapper = createBeanInstance(beanName, mbd, args); // ① 实例化
populateBean(beanName, mbd, instanceWrapper);             // ② 依赖注入
exposedObject = initializeBean(beanName, exposedObject, mbd); // ③ 初始化

4.3 初始化里干了啥?

java 复制代码
// initializeBean 内部顺序
invokeAwareMethods(beanName, bean);          // BeanNameAware 等
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
invokeInitMethods(beanName, wrappedBean, mbd); // @PostConstruct → InitializingBean → 自定义 init-method
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);

最后一步如果开启 AOP,会在这里返回代理对象。


5. 三级缓存与循环依赖(面试高频)

Spring 为了解决 单例循环依赖 ,在 doCreateBean 里引入三级缓存:

缓存 数据结构 作用
singletonObjects ConcurrentHashMap 成品 Bean
earlySingletonObjects ConcurrentHashMap 早期引用(已实例化但未初始化)
singletonFactories ConcurrentHashMap<String, ObjectFactory<?>> 工厂对象,可生成代理

流程:

  1. 实例化后,先把工厂放进 singletonFactories
  2. 如果发生循环依赖,提前执行工厂拿到引用,塞进 earlySingletonObjects
  3. 初始化完成后,升级成正式 Bean,放入 singletonObjects,清理其它两级。

6. 懒加载、原型、Bean 的区分

类型 何时创建
singleton + 非懒加载 finishBeanFactoryInitialization() 阶段统一创建
singleton + 懒加载 第一次 getBean 才创建
prototype 每次 getBean 都重新 doCreateBean
request/session WebApplicationContext 中对应作用域第一次访问时创建

7. 调试技巧一条命令

把日志开到 DEBUG,一眼看全:

yaml 复制代码
logging:
  level:
    org.springframework.beans.factory.support.DefaultListableBeanFactory: DEBUG
    org.springframework.beans.factory.support.AbstractBeanFactory: DEBUG

启动后你会看到:

arduino 复制代码
Creating instance of bean 'userService'
Finished creating instance of bean 'userService'

8. 小结

  1. 注册 :@ComponentScan 等把类变成 BeanDefinition → Map
  2. 实例化 :refresh() 末尾的 preInstantiateSingletons() 统一遍历,首次 getBean 才 doCreateBean
  3. 三级缓存:解决循环依赖,同时支持 AOP 提前代理
  4. 懒加载 / 原型:各自在第一次使用时才走创建链路
java 复制代码
getBean(beanName); // 没有就创建,创建完就放缓存
相关推荐
凤山老林21 分钟前
Spring Boot 启动冲刺:耗时剖析、懒加载策略与事件驱动初始化优化
spring boot·后端·python
朱涛的自习室1 小时前
我给 Gemini 4 和 DeepSeek 搞了套「末位淘汰」,AI 干活会更猛吗?(Day 3)
android·前端·后端
woniu_maggie4 小时前
SAP MDG主数据治理实战:从架构原理到项目实施全解析
后端
xiaoqiMikko4 小时前
Redis 实战(一):内存满了 —— 拒写、淘汰、被系统杀掉,以及删了 key 内存为什么不降(6.2.4 实测)
redis·后端
工程师小爱4 小时前
电芯尺寸合格,不等于电池包尺寸合格
后端
geovindu4 小时前
rust:Memento Pattern(续)
开发语言·后端·设计模式·rust·行为模式·备忘录模
闲云lazycloud4 小时前
第 1 课:Harness 与 Agent 运行时
人工智能·后端
熊哈哈O_o5 小时前
260+异构同步链路:DDL语法自动翻译在大规模数据同步中的实现原理
后端
用户9888321871745 小时前
别把 IOptionsSnapshot 注入单例:从生命周期看 .NET 配置读取
后端