用三条主线理解 Spring:BeanDefinition、生命周期与 AOP
如果只能用三条主线来理解 Spring 框架,那么一定是:
BeanDefinition 的加载、Bean 的生命周期、AOP 如何嵌入生命周期。
Spring 中绝大多数看起来"复杂"的机制,本质上都是这三点的组合与演化。
一、为什么要用"三条主线"来理解 Spring?
在学习 Spring 的过程中,很多人都会遇到类似困惑:
- 注解越来越多,却不知道它们到底在什么时候生效
- 自动装配看似神秘,不清楚真正的触发点
- 事务、AOP、循环依赖的问题反复出现
根本原因并不是 Spring 太复杂,而是没有从结构性视角去理解它。
Spring 并不是一个"以注解为中心"的框架,而是一个分阶段、强约束的 IoC 容器系统。从整体上看,它几乎所有能力都围绕三个阶段展开:
- 容器结构的构建阶段(BeanDefinition)
- 对象创建与管理阶段(Bean 生命周期)
- 方法行为增强阶段(AOP)
抓住这三条主线,绝大多数 Spring 行为都可以被反向推导出来。
二、第一条主线:BeanDefinition ------ 容器结构层
1. Spring 真正管理的不是 Bean,而是 BeanDefinition
在 Spring 容器启动的早期,并不会立刻创建任何业务对象。此时 Spring 关注的核心问题只有一个:
容器里应该有哪些 Bean,它们应该如何被创建?
这些信息不会以"对象"的形式存在,而是被统一抽象为:
text
BeanDefinition
你可以把 BeanDefinition 理解为一张"造 Bean 的说明书",其中包含:
- Bean 对应的 class
- scope(singleton / prototype)
- 是否懒加载
- 依赖关系
- 工厂方法
- 初始化 / 销毁方法
在这个阶段,容器中只有定义,没有实例。
2. BeanDefinition 阶段能做什么?
这是 Spring 启动过程中唯一可以改变容器结构的阶段。
在这一阶段,框架或用户代码可以:
- 新增 BeanDefinition
- 删除或替换已有 BeanDefinition
- 决定 Bean 是否注册
- 修改 scope、role、lazy 等元信息
一旦进入 Bean 生命周期阶段,容器会冻结配置,结构将不再允许被修改。
3. 为什么大量框架注解都工作在这一阶段?
因为只有在 BeanDefinition 阶段,注解才能真正影响"有没有这个 Bean"。
常见例子包括:
- MyBatis 的
@Mapper - Dubbo 的
@EnableDubbo - Spring Boot 的自动装配
这些注解背后,通常都会借助:
ImportSelectorImportBeanDefinitionRegistrarBeanDefinitionRegistryPostProcessor
它们的目标只有一个:
把注解携带的元信息,转换成 BeanDefinition 并注册进容器。
三、第二条主线:Bean 生命周期 ------ 对象管理层
当所有 BeanDefinition 注册完成并被冻结之后,Spring 才真正进入"创建对象"的阶段。
1. 生命周期的本质是什么?
从本质上看,Bean 生命周期做的事情只有一件:
把一次简单的
new操作,拆分成多个可插拔的阶段。
一个典型的单例 Bean,会经历如下步骤:
- 实例化(构造方法执行)
- 属性填充(依赖注入)
- Aware 接口回调
- 初始化前处理(
BeanPostProcessor) - 初始化方法(
@PostConstruct/afterPropertiesSet/ init-method) - 初始化后处理(
BeanPostProcessor) - 放入单例池
这种拆分,为 Spring 的扩展能力奠定了基础。
2. 生命周期为什么要拆得这么细?
因为 Spring 需要在对象创建的不同时间点,允许"外部逻辑"介入,例如:
- 依赖注入
- 循环依赖处理
- AOP 代理生成
- 容器感知能力(Aware)
如果对象一开始就被完整创建,这些能力将无法实现。
3. 循环依赖为什么只能 setter 注入解决?
Spring 解决循环依赖的前提条件是:
对象已经被实例化出来,即使它还没有完成初始化。
Setter / Field 注入的流程是:
text
实例化对象
↓
提前暴露对象引用
↓
依赖注入
而构造器注入在实例化阶段就需要完整依赖:
text
构造器执行
↓
需要依赖对象
此时对象尚不存在,Spring 无法提前暴露引用,因此无法解决构造器循环依赖。
四、第三条主线:AOP ------ 行为增强层
AOP 并不是一个独立于 IoC 的系统,而是深度嵌入 Bean 生命周期中的一种扩展机制。
1. AOP 真正发生在什么时候?
AOP 并不是在方法调用时"临时判断要不要增强",而是在 Bean 创建完成之后,就已经决定好要不要生成代理对象。
也就是说:
AOP 发生在 Bean 创建期,行为生效在方法调用期。
2. AOP 是如何嵌入生命周期的?
Spring AOP 通过实现 BeanPostProcessor,在 初始化后阶段 对 Bean 进行处理。
以事务为例:
@Transactional只是一个元数据注解- 真正起作用的是
TransactionInterceptor - 它会在
postProcessAfterInitialization阶段判断是否需要创建代理
如果需要,Spring 会返回代理对象,并用它替换原始 Bean。
3. 为什么事务、自调用会失效?
java
@Service
public class AService {
@Transactional
public void a() {
this.b();
}
@Transactional
public void b() {}
}
实际调用路径是:
text
外部调用 → 代理对象.a()
→ 目标对象.a()
→ this.b()
this.b() 并没有经过代理,自然也不会进入事务拦截器,因此事务不会生效。
4. AOP 与 BeanDefinition 的边界
AOP 只能:
- 包装 Bean
- 拦截方法调用
但它不能:
- 决定 Bean 是否存在
- 改变 Bean 的 scope
- 修改依赖结构
这些能力,全部属于 BeanDefinition 阶段。
五、用这三条主线反推 Spring 的一切行为
掌握这三条主线之后,很多问题都可以自然解释:
- 注解不生效 → BeanDefinition 是否被注册
- 循环依赖异常 → 是否构造器注入
- 事务不生效 → 是否走了代理
- Starter 无感接入 → BeanDefinition 阶段完成装配
六、一句话总结
Spring 的核心可以归纳为三件事:
第一,构建容器结构(BeanDefinition);
第二,按生命周期管理对象(Bean Lifecycle);
第三,在生命周期合适的阶段织入 AOP 行为增强。
当你从这个视角再回头看 Spring,会发现:
- 它并不神秘
- 也不全是"魔法"
- 而是一个被工程需求不断扩展、但主干极其清晰的框架
理解这三条主线,你就已经站在了 Spring 框架设计者的视角。