2026年7月13日 · 3 分钟阅读
手写 Spring 核心:从 IoC 容器到 AOP 与循环依赖
以最小实现串起 BeanDefinition、BeanFactory、依赖注入、Bean 生命周期、动态代理、三级缓存与 Spring 源码入口。
阅读 Spring 源码最有效的方式,不是逐行跟踪所有分支,而是先手写一个最小模型,再回到框架源码寻找对应角色。Spring 的主线可以压缩为两件事:IoC 管理对象,AOP 增强对象。
源码阅读路线
先明确入口和目标:应用从哪里进入框架、主流程解决什么问题、关键扩展点在哪里。建议依次阅读:
BeanFactory与ApplicationContext的职责。- BeanDefinition 的注册与读取。
- Bean 的实例化、属性填充和初始化。
- BeanPostProcessor 对生命周期的介入。
- AOP 代理对象的创建与调用。
- 单例循环依赖的处理。
不要一开始进入旁支。先画出主流程,再带着具体问题深入实现类。
手写一个最小 IoC 容器
IoC 容器至少需要四类角色:
配置/注解
-> BeanDefinitionReader
-> BeanDefinitionRegistry
-> BeanFactory
-> singletonObjects
- BeanDefinition:保存 Bean 类型、作用域、依赖和初始化信息。
- Registry:根据 beanName 保存 BeanDefinition。
- BeanFactory:按名称获取 Bean;不存在时负责创建。
- SingletonRegistry:缓存已经创建完成的单例对象。
最小的 getBean() 流程是:查单例缓存、读取定义、反射实例化、填充属性、执行初始化、写入单例缓存并返回。
当容器逐步完善后,可以继续加入 XML/注解解析、构造器选择、作用域、Aware 回调、初始化方法以及 BeanPostProcessor。此时再看 AbstractBeanFactory、DefaultListableBeanFactory 和 AbstractAutowireCapableBeanFactory,每个抽象就都有了落点。
Bean 生命周期的关键节点
一个常见的简化顺序是:
实例化
-> 属性填充
-> Aware 回调
-> BeanPostProcessor#postProcessBeforeInitialization
-> 初始化方法
-> BeanPostProcessor#postProcessAfterInitialization
-> 可用 Bean
Spring 的大量能力并没有硬编码进 Bean 创建主流程,而是通过后置处理器扩展。AOP、@Autowired、@Value 等机制都能从这里找到切入点。
从动态代理理解 AOP
AOP 的核心不是“切面”术语,而是用代理对象包裹目标对象,在方法调用前后执行增强逻辑。
- JDK 动态代理面向接口。
- CGLIB 通过生成子类代理普通类。
- Pointcut 判断哪些类和方法需要增强。
- Advice 描述增强逻辑。
- Advisor 把 Pointcut 与 Advice 组合起来。
容器创建 Bean 后,自动代理创建器会判断它是否匹配 Advisor;若匹配,最终放进容器的通常是代理对象。调用代理方法时,再依次执行拦截器链和目标方法。
为什么需要三级缓存
假设 A 依赖 B,B 又依赖 A。创建 A 时必须先暴露一个“早期引用”,才能让 B 完成属性注入。
| 缓存 | 作用 |
|---|---|
一级缓存 singletonObjects | 保存完整初始化后的单例 |
二级缓存 earlySingletonObjects | 保存已经生成的早期引用 |
三级缓存 singletonFactories | 保存用于生成早期引用的 ObjectFactory |
第三级缓存的意义不只是延迟取对象,还允许在需要时生成与最终形态一致的早期代理。它主要解决 setter/字段注入下的单例循环依赖,构造器循环依赖通常无法用这个机制解决。
总结
Spring 源码的骨架是:BeanDefinition 描述对象,BeanFactory 创建对象,生命周期回调扩展对象,BeanPostProcessor 改造对象,代理机制增强对象,三级缓存协调特殊的创建顺序。掌握这条主线后,再阅读源码中的模板方法和扩展接口会轻松很多。