Spring 源码系列(6): Bean 的诞生:从 getBean 到 doCreateBean 全链路
引子面试里另一道高频题“getBean()之后Spring 到底做了什么”很多人知道Bean 会被创建但说不清从getBean到真正拿到对象之间经过了doGetBean → getSingleton → createBean → doCreateBean这条链更说不清doCreateBean内部那段经典的三段式实例化createBeanInstance→ 属性填充populateBean→ 初始化initializeBean本篇把这条全链路走通并点出提前暴露这一循环依赖的关键伏笔第 9 篇会专门拆。一句话结论getBean是门面先查缓存单例只建一次查不到才进doCreateBean内部按实例化 → 提前暴露 → 填充 → 初始化四步产出对象最后放回一级缓存。前置知识getBean是BeanFactory接口的门面方法真正逻辑在AbstractBeanFactory.doGetBean。真正的创建动作在AbstractAutowireCapableBeanFactory.doCreateBean。doCreateBean的核心三段createBeanInstancenew 出来→populateBean注入属性→initializeBean回调与代理。一、getBean 到 doCreateBean 的调用链doGetBean的精简骨架关键分支已注释// AbstractBeanFactory.doGetBeanprotectedTTdoGetBean(Stringname,ClassTrequiredType,Object[]args,booleantypeCheckOnly){StringbeanNametransformedBeanName(name);// 1. 先查一级缓存已完全初始化的成品 BeanObjectsharedInstancegetSingleton(beanName);if(sharedInstance!nullargsnull){// 处理 FactoryBean拿到的是 getObject() 结果见第 10 篇beangetObjectForBeanInstance(sharedInstance,name,beanName,null);}else{// 2. 原型 Bean 正在创建 → 说明出现循环依赖直接抛异常if(isPrototypeCurrentlyInCreation(beanName)){thrownewBeanCurrentlyInCreationException(beanName);}// 3. 父类容器存在则委托 parent层级容器// ... parentBeanFactory.getBean(...)// 4. 单例调用 getSingleton 模板内部触发 createBeanif(mbd.isSingleton()){sharedInstancegetSingleton(beanName,()-createBean(beanName,mbd,args));beangetObjectForBeanInstance(sharedInstance,name,beanName,mbd);}// 5. 原型 / 其他作用域分支略}return(T)bean;}getSingleton(beanName, singletonFactory)这个模板方法是单例只建一次 循环依赖提前暴露的总开关publicObjectgetSingleton(StringbeanName,ObjectFactory?singletonFactory){synchronized(this.singletonObjects){ObjectsingletonObjectthis.singletonObjects.get(beanName);// 再加锁查一次if(singletonObjectnull){beforeSingletonCreation(beanName);// 标记正在创建singletonsCurrentlyInCreationtry{singletonObjectsingletonFactory.getObject();// ← 实际调用 createBean}finally{afterSingletonCreation(beanName);// 取消正在创建标记}addSingleton(beanName,singletonObject);// 放入一级缓存清二三级}returnsingletonObject;}}⚠️注意getSingleton的正在创建标记singletonsCurrentlyInCreation正是循环依赖能被识别的基础——B 要 A 时发现 A 已在创建中就走提前暴露分支而不是再 new 一个 A。二、doCreateBean 的三段式含提前暴露// AbstractAutowireCapableBeanFactory.doCreateBean精简protectedObjectdoCreateBean(StringbeanName,RootBeanDefinitionmbd,Object[]args){// 1. 实例化反射 / 工厂方法 new 出裸对象BeanWrapperinstanceWrappercreateBeanInstance(beanName,mbd,args);ObjectbeaninstanceWrapper.getWrappedInstance();// 2. 提前暴露解决循环依赖把工厂放进三级缓存booleanearlySingletonExposure(mbd.isSingleton()this.allowCircularReferencesisSingletonCurrentlyInCreation(beanName));if(earlySingletonExposure){addSingletonFactory(beanName,()-getEarlyBeanReference(beanName,mbd,bean));}// 3. 属性填充Autowired / XML property 在此populateBean(beanName,mbd,instanceWrapper);// 4. 初始化Aware → BPP 前置 → init → BPP 后置/AOPObjectexposedObjectinitializeBean(beanName,exposedObject,mbd);// 5. 循环依赖校验若早期引用与最终对象不一致如被代理且无依赖真正用到早期引用则抛异常returnexposedObject;}步骤方法干了什么关键扩展点① 实例化createBeanInstance推断构造器反射 new 实例SmartInstantiationAwareBeanPostProcessor.determineCandidateConstructors② 提前暴露addSingletonFactory把() - getEarlyBeanReference放进三级缓存仅单例且允许循环依赖③ 属性填充populateBean注入依赖AutowiredAnnotationBeanPostProcessor第 8 篇④ 初始化initializeBean回调与代理Aware/BeanPostProcessor/InitializingBean⑤ 校验末尾判断早期引用 vs 最终对象一致性不一致且无依赖使用则报错三、三段各自展开3.1 createBeanInstance对象怎么 new 出来若mbd指定了工厂方法factory-method走instantiateUsingFactoryMethod。否则推断构造器SmartInstantiationAwareBeanPostProcessor.determineCandidateConstructors决定用哪个构造器构造器注入在此解析见第 8 篇。用BeanUtils.instantiateClass(constructor, args)反射创建——最常见的是无参构造。3.2 populateBean属性从哪来protectedvoidpopulateBean(StringbeanName,RootBeanDefinitionmbd,BeanWrapperbw){// 1. InstantiationAwareBeanPostProcessor 后处理Autowired 在此触发for(InstantiationAwareBeanPostProcessorbp:getBeanPostProcessors()){PropertyValuespvsbp.postProcessProperties(pvs,bw.getWrappedInstance(),beanName);}// 2. 应用 XML 配置的 property 值applyPropertyValues(beanName,mbd,bw,pvs);}关键记忆点Autowired/Resource注入发生在populateBean即属性填充阶段不是initializeBean。这是第 8 篇的主角。3.3 initializeBean回调与代理的四阶段protectedObjectinitializeBean(StringbeanName,Objectbean,RootBeanDefinitionmbd){invokeAwareMethods(beanName,bean);// ① Aware 回调wrappedBeanapplyBeanPostProcessorsBeforeInitialization(...);// ② BPP 前置PostConstructinvokeInitMethods(beanName,wrappedBean,mbd);// ③ InitializingBean init-methodwrappedBeanapplyBeanPostProcessorsAfterInitialization(...);// ④ BPP 后置AOP 代理在此生成returnwrappedBean;}① 只回调BeanNameAware/BeanFactoryAware/BeanClassLoaderAware见第 7 篇。④ 若存在 AOP 自动代理创建器postProcessAfterInitialization会返回代理对象——你最终拿到的 Bean 可能已不是原始对象。四、流程图五、常见误区误区正解getBean一定创建新对象先查一级缓存单例只建一次提前暴露 提前初始化完成只暴露刚 new 出来的半成品AOP 代理在initializeBean才生成实例化好就立刻可被别人用需经populateBeaninitializeBean但循环依赖时靠三级缓存提前拿到半成品引用构造器注入的循环依赖能解不能——构造器在createBeanInstance阶段就要求依赖就绪而那时还没进三级缓存第 9 篇六、面试题自测从getBean到doCreateBean的完整调用链是什么doCreateBean的三段式实例化/填充/初始化各自对应哪个方法提前暴露发生在哪一步它的作用是什么initializeBean的四个阶段按什么顺序执行为什么构造器注入的循环依赖无法被 Spring 解决七、Debug 小技巧在第 2 篇的调试工程里在AbstractAutowireCapableBeanFactory.doCreateBean第一行打断电观察instanceWrapper出来后bean是裸对象未经填充/代理。在initializeBean前后分别打印bean的getClass().getName()若有 AOP会发现返回的是$$EnhancerBySpringCGLIB$$代理类证明代理在 ④ 阶段生成。在getSingleton的singletonFactory.getObject()处Step Into直接进createBean→doCreateBean看清模板方法触发创建的机制。下篇预告第 7 篇我们把initializeBean里那条生命周期完整展开实例化 → 属性填充 → 初始化 → 销毁把 Aware /PostConstruct/InitializingBean/ 销毁回调的顺序与源码一次性讲透。如果这篇对你有帮助欢迎点赞 · 收藏 · 关注三连支持。Spring 源码系列共 30 篇由浅入深持续更新中。有疑问或想深挖的源码点评论区告诉我下篇见。

相关新闻

如何高效使用FAST-LIVO2:从零开始的多传感器融合SLAM实战指南

如何高效使用FAST-LIVO2:从零开始的多传感器融合SLAM实战指南

如何高效使用FAST-LIVO2:从零开始的多传感器融合SLAM实战指南 【免费下载链接】FAST-LIVO2 FAST-LIVO2: Fast, Direct LiDAR-Inertial-Visual Odometry 项目地址: https://gitcode.com/gh_mirrors/fa/FAST-LIVO2 FAST-LIVO2是一个高效且精确的LiDAR-惯性-视觉…

2026/7/21 18:00:21阅读更多 →
开源NestJS实战:企业级TypeScript后端开发终极指南

开源NestJS实战:企业级TypeScript后端开发终极指南

开源NestJS实战:企业级TypeScript后端开发终极指南 【免费下载链接】awesome-nest-boilerplate Awesome NestJS Boilerplate 😍, Typescript 💪, Postgres 🎉, TypeORM 🥳 项目地址: https://gitcode.com/gh_mirrors…

2026/7/21 18:00:21阅读更多 →
如何用Ruflo构建AI代理协作系统:从零开始的完整指南

如何用Ruflo构建AI代理协作系统:从零开始的完整指南

如何用Ruflo构建AI代理协作系统:从零开始的完整指南 【免费下载链接】ruflo 🌊 The leading agent meta-harness. Deploy intelligent multi-player swarms, coordinate autonomous workflows, and build conversational AI systems. Features adaptive …

2026/7/21 18:00:21阅读更多 →
504错误解析:从技术原理到人生隐喻

504错误解析:从技术原理到人生隐喻

1. 项目概述:当504错误成为人生隐喻 "504 Gateway Time-out"这个技术术语最近在社交媒体上意外走红,但它被赋予的解读角度却令人耳目一新——"并非卡顿,他们加载更好的自己"。这个创意将服务器响应超时的技术故障&#x…

2026/7/21 22:54:49阅读更多 →
三菱PLC MC协议通信与1E3E帧解析

三菱PLC MC协议通信与1E3E帧解析

1. 三菱PLC MC协议通信基础解析三菱PLC的MC协议(Mitsubishi Communication Protocol)是工业自动化领域广泛使用的通信标准,它定义了上位机与PLC之间数据交换的规则和格式。在实际工业控制系统中,这种通信方式承担着关键的数据采集…

2026/7/21 22:54:49阅读更多 →
仅限前500名开发者获取:2024最全AI模型RTT Benchmark数据集(含vLLM/TGI/Ollama三框架实测+硬件配置清单)

仅限前500名开发者获取:2024最全AI模型RTT Benchmark数据集(含vLLM/TGI/Ollama三框架实测+硬件配置清单)

更多请点击: https://codechina.net 第一章:AI模型 响应速度对比 在实际生产环境中,AI模型的响应速度直接影响用户体验与系统吞吐能力。本章聚焦于主流开源大语言模型在相同硬件(NVIDIA A10G GPU,32GB显存&#xff09…

2026/7/21 22:54:49阅读更多 →
AI数字人接入Unity游戏引擎:从语音克隆到实时对话NPC的工程实践

AI数字人接入Unity游戏引擎:从语音克隆到实时对话NPC的工程实践

1. 项目概述:当AI数字人遇见游戏引擎最近在AI数字人领域,Linly-Talker这个开源项目热度不低,它整合了语音克隆、口型同步和实时对话,让静态的图片或3D模型“开口说话”。作为一个经常在游戏开发和AI应用之间“反复横跳”的技术从业…

2026/7/21 22:54:49阅读更多 →
手把手教你搭建货运搬家系统

手把手教你搭建货运搬家系统

博主介绍: 所有项目都配有从入门到精通的安装教程,可二开,提供核心代码讲解,项目指导。 项目配有对应开发文档、解析等 项目都录了发布和功能操作演示视频;项目的界面和功能都可以定制,包安装运行&#xff…

2026/7/21 22:54:49阅读更多 →
Windows 11 26H1更新亮点与优化指南

Windows 11 26H1更新亮点与优化指南

1. Windows 11 26H1五月更新深度解析微软最新推送的Windows 11 26H1版本(Build 28000.2113)带来了多项功能增强和系统优化。这次更新特别针对搭载高通骁龙X2系列处理器的ARM架构设备进行了专项优化,同时也为所有用户提供了更完善的辅助功能和…

2026/7/21 22:52:49阅读更多 →
Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/21 0:51:49阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/21 0:51:49阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/21 0:51:49阅读更多 →
Windows+macOS 通用 OpenClaw 部署流程,内置依赖一键启动智能桌面助手

Windows+macOS 通用 OpenClaw 部署流程,内置依赖一键启动智能桌面助手

📌教程适配:OpenClaw v2.7.9 | 兼容 Windows10/11、macOS 双系统 📖前言 当下各类本地 AI 工具层出不穷,多数产品仅能完成文字问答交互,很难直接操控电脑执行实际操作。OpenClaw,业内常称小龙虾 AI&#…

2026/7/21 0:01:46阅读更多 →
Codex 接入后 Bug 反增?复盘从个人演示到团队协作的“流程陷阱”

Codex 接入后 Bug 反增?复盘从个人演示到团队协作的“流程陷阱”

聊《一次Codex项目复盘,问题最后出在流程而不是模型》之前,先说一句实在的:别急着背概念,先看它在真实项目里到底解决什么问题。摘要先把这篇文章的目标说清楚:看完之后,你应该能判断这件事值不值得做&…

2026/7/21 0:01:46阅读更多 →
手把手搓一个五子棋游戏,零代码也能当“游戏开发者”

手把手搓一个五子棋游戏,零代码也能当“游戏开发者”

大家好,还是我。前几期带大家做了心情日记本和可视化大屏,后台有朋友留言:“能不能教点好玩的?我想做游戏,但一行代码都不会。”行,这期就安排。今天的目标:从零做一个五子棋游戏。 带AI对战、三…

2026/7/21 0:03:46阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/21 22:53:50阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/21 18:53:30阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/21 18:53:30阅读更多 →