# 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); // 没有就创建,创建完就放缓存
相关推荐
知其然亦知其所以然2 小时前
MySQL 社招必考题:如何优化查询过程中的数据访问?
后端·mysql·面试
用户4099322502122 小时前
FastAPI秒杀库存总变负数?Redis分布式锁能帮你守住底线吗
后端·ai编程·trae
掘金酱2 小时前
🎉 2025年8月金石计划开奖公示
前端·人工智能·后端
SimonKing2 小时前
接口调用总失败?试试Spring官方重试框架Spring-Retry
java·后端·程序员
Cache技术分享2 小时前
191. Java 异常 - 捕获与处理异常
前端·后端
努力的小郑2 小时前
从一次分表实践谈起:我们真的需要复杂的分布式ID吗?
分布式·后端·面试
RoyLin2 小时前
TypeScript设计模式:模板方法模式
前端·后端·node.js
GeekAGI2 小时前
如何重复执行 curl 请求:8种实用方法详解
后端
LH_R2 小时前
OneTerm开源堡垒机实战(三):功能扩展与效率提升
运维·后端·安全