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

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 篇,由浅入深持续更新中。有疑问或想深挖的源码点,评论区告诉我,下篇见。