当前位置: 首页 > news >正文

Spring 源码系列(8): 依赖注入(DI)在源码里如何发生

引子

你只写了@Autowired private UserService userService;,对象就自动出现了。这背后 Spring 到底做了什么?

本篇回答三件事:① DI 发生在哪一步;②@Autowired@Resource的区别在源码层如何体现;③字段 / 构造器 / setter 注入为何"出生在不同阶段"——这直接决定了第 9 篇"循环依赖为何只有字段注入能解"。

💡一句话结论
字段/setter 注入发生在populateBean阶段,由AutowiredAnnotationBeanPostProcessor.postProcessProperties找到注入点,经resolveDependency → getBean(依赖)递归拿到(或创建)依赖对象,最后field.set注入。构造器注入则更早,在createBeanInstance推断构造器时就解析依赖。

前置知识

  1. DI 的本质是"在合适时机,把依赖对象 set 进当前 Bean"。
  2. 处理 DI 的是InstantiationAwareBeanPostProcessor(一类特殊的BeanPostProcessor):
    • AutowiredAnnotationBeanPostProcessor:处理@Autowired/@Value
    • CommonAnnotationBeanPostProcessor:处理@Resource/@PostConstruct/@PreDestroy
  3. 这些 BPP 在第 5 篇第 6 步registerBeanPostProcessors被注册进容器。

一、字段注入发生在 populateBean

// AbstractAutowireCapableBeanFactory.populateBean(精简)protectedvoidpopulateBean(StringbeanName,RootBeanDefinitionmbd,BeanWrapperbw){// 遍历 InstantiationAwareBeanPostProcessor,触发注入for(InstantiationAwareBeanPostProcessorbp:getBeanPostProcessors()){PropertyValuespvs=bp.postProcessProperties(pvs,bw.getWrappedInstance(),beanName);// @Autowired 就是在这一行完成的}// XML <property> 配置的值应用applyPropertyValues(beanName,mbd,bw,pvs);}

二、AutowiredAnnotationBeanPostProcessor 如何注入

postProcessProperties→ 找注入点 → 逐个注入:

// AutowiredAnnotationBeanPostProcessorpublicPropertyValuespostProcessProperties(PropertyValuespvs,Objectbean,StringbeanName){// 1. 取出缓存的注入元数据(@Autowired 字段/方法)InjectionMetadatametadata=findAutowiringMetadata(beanName,bean.getClass());// 2. 逐个注入metadata.inject(bean,beanName,pvs);returnpvs;}

注入一个字段时(AutowiredFieldElement.inject):

protectedvoidinject(Objectbean,StringbeanName,PropertyValuespvs){Fieldfield=this.field;Objectvalue=resolveFieldValue(field,bean,beanName);// ← 关键:解析依赖ReflectionUtils.makeAccessible(field);field.set(bean,value);// 反射设值}

resolveFieldValue最终会走到DefaultListableBeanFactory.resolveDependency

resolveFieldValue └─ beanFactory.resolveDependency(descriptor, beanName, ...) └─ doResolveDependency(...) ├─ 按类型找到候选 Bean(可能有多个,再按 @Qualifier / 名称筛选) └─ resolveCandidate(beanName, ...) └─ beanFactory.getBean(beanName) // ← 递归:依赖没创建就先创建它

⚠️关键getBean(依赖)这一行就是"依赖注入的本质"——也是循环依赖产生的根源(A 注入 B,B 又注入 A,二者互相等待)。第 9 篇专门解决它。

三、@Autowired vs @Resource

维度@Autowired@Resource
来源Spring 自带(org.springframework.beans.factory.annotationJSR-250 标准(javax.annotation
默认匹配按类型(byType)**按名称(byName)**优先,失败再 byType
配合限定@Qualifier("name")指定名称name属性直接指定
必需性required属性(默认 true)无 required,找不到直接报错
处理类AutowiredAnnotationBeanPostProcessorCommonAnnotationBeanPostProcessor

四、三种注入方式的"出生阶段"

注入方式发生在哪源码位置能否解循环依赖
字段注入populateBeanAutowiredFieldElement.inject✅ 能(靠三级缓存)
setter 注入populateBeanAutowiredMethodElement.inject✅ 能
构造器注入createBeanInstancedetermineCandidateConstructors+ 构造时getBean依赖❌ 不能

📌为什么构造器注入不能解循环依赖?
构造器注入在createBeanInstance(实例化)阶段就要求依赖对象已经存在,而此时当前 Bean 还没执行到addSingletonFactory(三级缓存注册),依赖方取不到"提前暴露的引用",只能再去创建当前 Bean,形成无限递归 →BeanCurrentlyInCreationException

五、DI 流程图

六、常见误区

误区正解
@Autowired按名称注入默认按类型,需配合@Qualifier才按名称
@Resource是 Spring 注解是 JSR-250 标准注解,由CommonAnnotationBeanPostProcessor处理
构造器注入也能解循环依赖不能,见上文
DI 发生在initializeBean字段/setter 注入在populateBean,早于初始化

七、面试题自测

  1. @Autowired在 Bean 生命周期的哪一步注入?
  2. resolveDependency最终调用了什么方法创建依赖?
  3. @Autowired@Resource的核心区别?
  4. 字段、setter、构造器三种注入分别发生在哪个阶段?为什么只有前两种能解循环依赖?
  5. 有多个同类型 Bean 时,@Autowired如何筛选出目标(提示:@Qualifier/ 优先级)?

八、Debug 小技巧

  1. AutowiredAnnotationBeanPostProcessor.postProcessProperties打断电,Step Intometadata.injectresolveFieldValue,看value是如何从getBean拿到的。
  2. 故意制造循环依赖(A 构造器注入 B,B 构造器注入 A),观察异常栈最顶端是BeanCurrentlyInCreationException,且发生在createBeanInstance阶段。
  3. resolveCandidategetBean处打条件断点beanName.equals("依赖名"),验证"注入即触发依赖的创建"。

下篇预告

第 9 篇,爆款预定:循环依赖终极拷问——为什么用三级缓存而不是二级。把本篇埋下的getBean递归与"提前暴露"彻底讲透。


如果这篇对你有帮助,欢迎点赞 · 收藏 · 关注三连支持。

Spring 源码系列共 30 篇,由浅入深持续更新中。有疑问或想深挖的源码点,评论区告诉我,下篇见。

http://www.jsqmd.com/news/1251088/

相关文章:

  • 天津包车哪家靠谱?用7个比较维度挑人,附2026正规天津包车排名与前三名电话 - 米米Ada
  • 2026宁波冷暖设备选购场景 解读宁波冷热机哪家好实用逻辑 - 起跑123
  • 星星火火音乐节青岛站阵容官宣!即将正式开票,向海而行,让热爱发光!
  • 2026年广西学子适配的加拿大留学班推荐实用全指南 - 热点品牌推荐
  • 2026年了解重庆合规代账哪家好的真实体验心得 - 起跑123
  • 2026年选购高性价比车用尿素灌装机 靠谱销售厂家怎么挑 - 热点品牌推荐
  • AI 电动抹布清洁机智能功率 MOSFET 完整选型方案
  • Demo 跑通不敢上线?权限与日志才是运维转 Agent 的生死线
  • 太原CMA甲醛检测公司怎么选:只测不除的专业实验室——国康CMA检测及公共卫生检测 - 信誉隆金银铂奢回收
  • 金属材料表面六种缺陷类型数据集分享(适用于YOLO系列深度学习分类检测任务)
  • 影刀RPA 自动化测试入门:用RPA做冒烟测试
  • 2026年7月发布威能壁挂炉官网售后服务电话24小时全新专属热线升级公示最新公告 - 故障代码查询
  • 【经济、大数据、人工智能可投、EI稳定检索】第五届信息经济、数据建模与云计算国际学术会议(ICIDC2026)
  • 2026年西北高适配钛合金阳台门源头厂家推荐指南 - 热点品牌推荐
  • 2026主流小程序制作平台对比出炉!看看哪个更合你心意?
  • 亲身到店探访天津卡地亚售后服务中心|全新维修地址与售后热线(2026年7月最新) - 卡地亚服务中心
  • AI智能体开发:技术现状、学习路径与职业前景
  • 佛山本地防水补漏精选TOP5推荐:正规漏水检测维修公司上门师傅推荐:厕所/棚顶/屋面/飘窗/阳台/地下室/厨房渗漏水精准测漏维修(2026最新) - 即刻修防水
  • [特殊字符]《淘宝TOP增值API聚石塔外禁调:CRM/数据罗盘为什么要签增值协议?》(附Python源码)
  • 影刀RPA 自动化简历筛选系统:HR提效实战
  • LangChain 实战指南到底解决了什么问题?
  • 2026实测:抖音GIF动图怎么去水印?教你几个不损画质的实用方法 - 免费软件工具方法教程
  • 设计EDA研发总监——八维度综合人才评估报告
  • 泰安CMA甲醛检测公司怎么选:只测不除的专业第三方实验室——清诚测研检测及公共卫生检测 - 信誉隆金银铂奢回收
  • 2026年金水区卫生间楼板滴水防水服务企业挑选实用攻略 - 热点品牌推荐
  • 计算机毕业设计选题推荐:2026年5个校园场景项目(SSM/SpringBoot+小程序+论文+源码)
  • 2026ai建站口碑好的企业推荐都有哪些,快来看看吧!
  • 重庆旧房改造家装公司口碑保障 本地消费选购实用指南 - 热点品牌推荐
  • 2026重庆大学城商务宴请火锅主流品牌综合测评报告 - 起跑123
  • 手机上换证件照底色的5个方法,第3个用过都说快 - AI测评专家