# 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); // 没有就创建,创建完就放缓存
相关推荐
子兮曰1 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰1 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
爱勇宝1 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
胡写代码1 天前
别再前后端各写一套表单校验了
java·后端
大勇前进1 天前
原生 PHP 还是 Laravel?小项目到底要不要上框架
后端
yuzhi_liu1 天前
我用 LangGraph4j 实现 Multi-Agent Supervisor
后端
alsmile1 天前
Node-RED 之外,国产规则引擎的新方案:基于标准语法,Go 先行实现
后端·开源·go
大白801 天前
PHP 内存溢出排查思路:看懂报错日志,精准定位问题
后端
二月龙1 天前
PHP 接口返回统一响应封装,让前后端对接更省心
后端
盖伦发发1 天前
软件工程SOLID 五大设计原则
后端·软件工程