引子
你只写了@Autowired private UserService userService;,对象就自动出现了。这背后 Spring 到底做了什么?
本篇回答三件事:① DI 发生在哪一步;②@Autowired和@Resource的区别在源码层如何体现;③字段 / 构造器 / setter 注入为何"出生在不同阶段"——这直接决定了第 9 篇"循环依赖为何只有字段注入能解"。
💡一句话结论
字段/setter 注入发生在populateBean阶段,由AutowiredAnnotationBeanPostProcessor.postProcessProperties找到注入点,经resolveDependency → getBean(依赖)递归拿到(或创建)依赖对象,最后field.set注入。构造器注入则更早,在createBeanInstance推断构造器时就解析依赖。
前置知识
- DI 的本质是"在合适时机,把依赖对象 set 进当前 Bean"。
- 处理 DI 的是
InstantiationAwareBeanPostProcessor(一类特殊的BeanPostProcessor):AutowiredAnnotationBeanPostProcessor:处理@Autowired/@ValueCommonAnnotationBeanPostProcessor:处理@Resource/@PostConstruct/@PreDestroy
- 这些 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.annotation) | JSR-250 标准(javax.annotation) |
| 默认匹配 | 按类型(byType) | **按名称(byName)**优先,失败再 byType |
| 配合限定 | @Qualifier("name")指定名称 | name属性直接指定 |
| 必需性 | required属性(默认 true) | 无 required,找不到直接报错 |
| 处理类 | AutowiredAnnotationBeanPostProcessor | CommonAnnotationBeanPostProcessor |
四、三种注入方式的"出生阶段"
| 注入方式 | 发生在哪 | 源码位置 | 能否解循环依赖 |
|---|---|---|---|
| 字段注入 | populateBean | AutowiredFieldElement.inject | ✅ 能(靠三级缓存) |
| setter 注入 | populateBean | AutowiredMethodElement.inject | ✅ 能 |
| 构造器注入 | createBeanInstance | determineCandidateConstructors+ 构造时getBean依赖 | ❌ 不能 |
📌为什么构造器注入不能解循环依赖?
构造器注入在createBeanInstance(实例化)阶段就要求依赖对象已经存在,而此时当前 Bean 还没执行到addSingletonFactory(三级缓存注册),依赖方取不到"提前暴露的引用",只能再去创建当前 Bean,形成无限递归 →BeanCurrentlyInCreationException。
五、DI 流程图
六、常见误区
| 误区 | 正解 |
|---|---|
@Autowired按名称注入 | 默认按类型,需配合@Qualifier才按名称 |
@Resource是 Spring 注解 | 是 JSR-250 标准注解,由CommonAnnotationBeanPostProcessor处理 |
| 构造器注入也能解循环依赖 | 不能,见上文 |
DI 发生在initializeBean | 字段/setter 注入在populateBean,早于初始化 |
七、面试题自测
@Autowired在 Bean 生命周期的哪一步注入?resolveDependency最终调用了什么方法创建依赖?@Autowired与@Resource的核心区别?- 字段、setter、构造器三种注入分别发生在哪个阶段?为什么只有前两种能解循环依赖?
- 有多个同类型 Bean 时,
@Autowired如何筛选出目标(提示:@Qualifier/ 优先级)?
八、Debug 小技巧
- 在
AutowiredAnnotationBeanPostProcessor.postProcessProperties打断电,Step Intometadata.inject→resolveFieldValue,看value是如何从getBean拿到的。 - 故意制造循环依赖(A 构造器注入 B,B 构造器注入 A),观察异常栈最顶端是
BeanCurrentlyInCreationException,且发生在createBeanInstance阶段。 - 在
resolveCandidate的getBean处打条件断点beanName.equals("依赖名"),验证"注入即触发依赖的创建"。
下篇预告
第 9 篇,爆款预定:循环依赖终极拷问——为什么用三级缓存而不是二级。把本篇埋下的getBean递归与"提前暴露"彻底讲透。
如果这篇对你有帮助,欢迎点赞 · 收藏 · 关注三连支持。
Spring 源码系列共 30 篇,由浅入深持续更新中。有疑问或想深挖的源码点,评论区告诉我,下篇见。