2026年7月13日 · 3 分钟阅读

手写 Spring 核心:从 IoC 容器到 AOP 与循环依赖

以最小实现串起 BeanDefinition、BeanFactory、依赖注入、Bean 生命周期、动态代理、三级缓存与 Spring 源码入口。

阅读 Spring 源码最有效的方式,不是逐行跟踪所有分支,而是先手写一个最小模型,再回到框架源码寻找对应角色。Spring 的主线可以压缩为两件事:IoC 管理对象,AOP 增强对象。

源码阅读路线

先明确入口和目标:应用从哪里进入框架、主流程解决什么问题、关键扩展点在哪里。建议依次阅读:

  1. BeanFactoryApplicationContext 的职责。
  2. BeanDefinition 的注册与读取。
  3. Bean 的实例化、属性填充和初始化。
  4. BeanPostProcessor 对生命周期的介入。
  5. AOP 代理对象的创建与调用。
  6. 单例循环依赖的处理。

不要一开始进入旁支。先画出主流程,再带着具体问题深入实现类。

手写一个最小 IoC 容器

IoC 容器至少需要四类角色:

配置/注解
  -> BeanDefinitionReader
  -> BeanDefinitionRegistry
  -> BeanFactory
  -> singletonObjects
  • BeanDefinition:保存 Bean 类型、作用域、依赖和初始化信息。
  • Registry:根据 beanName 保存 BeanDefinition。
  • BeanFactory:按名称获取 Bean;不存在时负责创建。
  • SingletonRegistry:缓存已经创建完成的单例对象。

最小的 getBean() 流程是:查单例缓存、读取定义、反射实例化、填充属性、执行初始化、写入单例缓存并返回。

当容器逐步完善后,可以继续加入 XML/注解解析、构造器选择、作用域、Aware 回调、初始化方法以及 BeanPostProcessor。此时再看 AbstractBeanFactoryDefaultListableBeanFactoryAbstractAutowireCapableBeanFactory,每个抽象就都有了落点。

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 改造对象,代理机制增强对象,三级缓存协调特殊的创建顺序。掌握这条主线后,再阅读源码中的模板方法和扩展接口会轻松很多。