Spring 自动注入是怎么实现的?从 @Component 到 @Autowired 的完整流程
你写过这样的代码:
@Component
public class UserService { }@Service
public class OrderService {@Autowiredprivate UserService userService;
}
没手动 new
,也没赋值,但 userService
就能直接用。
这背后不是魔法,而是一套精心分层、各司其职的自动化机制。
下面,我们用最清晰的逻辑,回答几个核心问题:
1. 容器启动时,谁做了什么?
谁:ClassPathBeanDefinitionScanner
(组件扫描器)
做了什么:在应用启动时,扫描你标记了 @Component
、@Service
的类,把它们转换成 BeanDefinition(Bean 的“蓝图”),注册进容器。
怎么做:通过反射读取类上的注解,提取类名、作用域、是否懒加载等信息,存入 DefaultListableBeanFactory
内部的 beanDefinitionMap
。
为什么:Spring 必须先知道“有哪些 Bean 要管理”,才能决定何时创建、如何注入。
不这么做会怎样:容器根本不知道 UserService
是个 Bean,后续一切注入无从谈起。
✅ 此时 还没创建任何对象,只有“菜谱”。
2. Bean 是什么时候创建的?谁创建的?
谁:DefaultListableBeanFactory
(Bean 工厂)
做了什么:在容器刷新(refresh()
)的最后阶段,调用 preInstantiateSingletons()
,提前创建所有非 @Lazy
的单例 Bean。
怎么做:
- 遍历所有
BeanDefinition
; - 对每个
singleton + 非懒加载
的 Bean,调用getBean("userService")
; - 最终通过
AbstractAutowireCapableBeanFactory.doCreateBean()
执行new UserService()
; - 创建完成后,把实例存入
singletonObjects
(一级缓存)。
为什么:提前暴露问题(如依赖缺失、循环依赖),避免运行时突然崩溃。
不这么做会怎样:所有 Bean 延迟到第一次使用才创建,启动快但运行时可能报错,调试困难。
✅
@Lazy
的 Bean 会跳过这一步,等到第一次被依赖或getBean()
时才创建。
3. @Autowired
是怎么“塞”进去的?
谁:AutowiredAnnotationBeanPostProcessor
(自动注入处理器)
做了什么:在 OrderService
对象创建后、初始化前,扫描它的字段,发现 @Autowired private UserService userService
,然后从容器中拿到 userService
实例,通过反射赋值塞进去。
怎么做:
- 调用
beanFactory.getBean("userService")
获取已创建的实例(来自singletonObjects
); - 调用
field.set(orderService, userService)
完成注入。
为什么:实现“声明式依赖”,开发者无需手动管理对象关系。
不这么做会怎样:userService
字段永远是 null
,调用时直接 NullPointerException
。
✅ 这个过程发生在
populateBean()
阶段,是doCreateBean()
的一部分。
4. 如果 A 依赖 B,B 又依赖 A,怎么办?
谁:DefaultSingletonBeanRegistry
(单例注册表)
做了什么:通过三级缓存机制,在 A 还没完全创建好时,就允许 B 引用 A 的“早期引用”。
三级缓存是:
- 一级
singletonObjects
:成品 Bean(最终放入容器的对象); - 二级
earlySingletonObjects
:已解析的早期引用; - 三级
singletonFactories
:ObjectFactory
,按需生成早期引用(支持 AOP 代理)。
怎么做:
- 创建 A 时,先
new A()
,然后立即注册一个ObjectFactory
到三级缓存; - 当 B 需要 A 时,从三级缓存拿到工厂,调用
getObject()
得到 A(可能是代理); - 这个早期引用会被缓存到二级,避免重复生成;
- A 完全初始化后,放入一级缓存,清除二、三级。
为什么:既要解决循环依赖,又要保证 AOP 代理一致性(B 拿到的必须是代理对象)。
不这么做会怎样:
- 没有缓存 → 循环依赖直接 StackOverflow;
- 只有两级缓存(直接存原始对象)→ B 拿到原始 A,容器最终存代理 A → 同一个 Bean 有两个实例,事务失效。
总结
- 容器启动:
Scanner
扫描 → 注册BeanDefinition
(菜谱); - Bean 创建:
BeanFactory
预实例化 →doCreateBean
→ 存入singletonObjects
; - 依赖注入:
AutowiredAnnotationBeanPostProcessor
反射赋值; - 循环依赖:
DefaultSingletonBeanRegistry
用三级缓存安全暴露早期引用。
这一切,都是确定的、可追踪的、有源码可证的工程逻辑,不是玄学。
下次再看到 @Autowired
,你就知道:
是谁,在哪一步,用什么方法,把哪个对象,放进了你的字段里。