# 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 行 显式转换 beanFactoryDefaultListableBeanFactory
循环 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() 统一遍历,首次 getBeandoCreateBean
  3. 三级缓存:解决循环依赖,同时支持 AOP 提前代理
  4. 懒加载 / 原型:各自在第一次使用时才走创建链路
java 复制代码
getBean(beanName); // 没有就创建,创建完就放缓存
相关推荐
大阿明4 小时前
Spring Boot(快速上手)
java·spring boot·后端
墨香幽梦客5 小时前
API集成技术规范:RESTful与GraphQL在企业系统对接中的应用对比
后端·restful·graphql
刀法如飞6 小时前
AI编程时代,为什么35岁以上程序员会更吃香?
人工智能·后端·ai编程
小码哥_常6 小时前
Spring Boot 遇上 HMAC-SHA256,API 安全大升级!
后端
小码哥_常6 小时前
10分钟极速掌握!SpringBoot+Vue3整合SSE实现实时消息推送
后端
大黄说说7 小时前
深入 Go 语言 GMP 调度模型:高并发的秘密武器
后端
云原生指北8 小时前
Omnipub E2E 测试文章 - 自动化验证
后端
IT_陈寒8 小时前
SpringBoot自动配置揭秘:5个让开发效率翻倍的隐藏技巧
前端·人工智能·后端
添尹9 小时前
Go语言基础之数组
后端·golang
luom010210 小时前
SpringBoot - Cookie & Session 用户登录及登录状态保持功能实现
java·spring boot·后端