getBean源码实战总结
代码仓库 :Gitee 仓库链接
本文档是对 getBean 源码实战系列文章的总结,建议先阅读完整的系列文章后再阅读本文。
回顾整个系列
从getBean源码实战(一)开始,我们一步步深入 Spring 的 getBean() 方法,探索了 Bean 获取的完整流程。现在让我们回顾一下整个系列的核心内容:
系列文章概览
- (一)基础流程 - 最简单的 Bean 获取,理解
doGetBean的基本流程 - (二)别名解析 - 通过别名获取已缓存的 Bean,理解别名转换和一级缓存
- (三)循环依赖 - 单例 Bean 的循环依赖处理,理解三级缓存机制
- (四)FactoryBean -
FactoryBean的特殊处理,理解工厂 Bean 的机制 - (五)MergedBeanDefinition - Bean 定义合并,理解父子 Bean 定义的继承
- (六)BeanAware - Aware 接口回调机制,理解 Bean 如何感知容器信息
💡 关键理解 :这六篇文章从不同角度深入分析了 getBean() 方法,每一篇都聚焦一个核心机制,但它们是相互关联的,共同构成了 Spring IoC 容器的 Bean 获取机制。
getBean 的完整流程
让我们从整体上理解 getBean() 的完整流程,这是整个系列的核心:
核心流程图
getBean(name)
↓
doGetBean(name, requiredType, args, typeCheckOnly)
↓
【步骤1】transformedBeanName(name) - 翻译 Bean 名称
├─ 处理 FactoryBean 前缀 (&)
└─ 处理别名转换 (canonicalName)
↓
【步骤2】getSingleton(beanName) - 从缓存获取单例
├─ 一级缓存:singletonObjects(完全初始化的单例)
├─ 二级缓存:earlySingletonObjects(早期引用)
└─ 三级缓存:singletonFactories(ObjectFactory)
↓
【步骤3】getMergedLocalBeanDefinition(beanName) - 获取合并后的 Bean 定义
├─ 处理父子 Bean 定义继承
└─ 合并属性、依赖等
↓
【步骤4】检查依赖关系 (dependsOn)
↓
【步骤5】根据作用域创建 Bean
├─ 单例 (singleton)
│ ├─ getSingleton(beanName, singletonFactory)
│ │ ├─ 标记为正在创建
│ │ ├─ 创建 Bean 实例 (createBean)
│ │ │ ├─ doCreateBean
│ │ │ │ ├─ 实例化 (createBeanInstance)
│ │ │ │ ├─ 提前暴露(放入三级缓存)
│ │ │ │ ├─ 属性注入 (populateBean)
│ │ │ │ │ └─ 触发依赖 Bean 的创建(可能形成循环依赖)
│ │ │ │ └─ 初始化 (initializeBean)
│ │ │ │ ├─ invokeAwareMethods(Aware 接口)
│ │ │ │ ├─ BeanPostProcessor(前置处理)
│ │ │ │ ├─ invokeInitMethods(初始化方法)
│ │ │ │ └─ BeanPostProcessor(后置处理)
│ │ │ └─ 放入一级缓存
│ │ └─ 从缓存中移除
│ └─ 返回单例 Bean
├─ 原型 (prototype)
│ └─ 每次创建新实例
└─ 其他作用域(request、session 等)
↓
【步骤6】getObjectForBeanInstance - 处理 FactoryBean
├─ 判断是否需要获取 FactoryBean 本身
└─ 获取 FactoryBean 创建的对象
↓
返回 Bean 实例
🔍 观察:这个流程看似复杂,但实际上每一步都有其明确的目的。让我们深入理解每个关键环节。
核心机制深度解析
1. 名称转换机制
核心方法 :transformedBeanName() 和 canonicalName()
java
protected String transformedBeanName(String name) {
return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}
💡 关键理解:名称转换分为两步:
- 处理 FactoryBean 前缀 :
&beanName→beanName - 处理别名转换 :
alias→canonicalName(支持链式别名)
设计思想:
- 统一入口:所有名称转换都通过这个方法,保证一致性
- 链式别名支持:别名可以指向另一个别名,最终指向 Bean 名称
- FactoryBean 特殊处理 :通过
&前缀区分获取 FactoryBean 本身还是其创建的对象
2. 三级缓存机制
核心缓存:
- 一级缓存(singletonObjects):完全初始化的单例 Bean
- 二级缓存(earlySingletonObjects):早期引用,用于解决循环依赖
- 三级缓存(singletonFactories):ObjectFactory,用于创建早期引用
💡 关键理解:三级缓存是 Spring 解决单例循环依赖的核心机制。
为什么需要三级缓存?
让我们思考一下:
如果只有一级缓存:
- 问题:Bean A 依赖 Bean B,Bean B 依赖 Bean A
- 当创建 A 时,需要注入 B,但 B 还没创建
- 当创建 B 时,需要注入 A,但 A 还没完全初始化
- 结果:无法解决循环依赖
如果只有一级和二级缓存:
- 问题:无法处理 AOP 代理的情况
- 当 Bean 需要 AOP 代理时,早期引用应该是代理对象,而不是原始对象
- 结果:无法正确处理需要代理的循环依赖
三级缓存的作用:
- 延迟创建早期引用:只有在真正需要时才通过 ObjectFactory 创建
- 支持 AOP 代理:ObjectFactory 中可以应用 BeanPostProcessor,创建代理对象
- 性能优化:不需要代理的 Bean 不会创建代理对象
🔍 发现:三级缓存的设计体现了 Spring 在功能性和性能之间的精妙平衡。
3. 循环依赖解决机制
核心流程(以 ServiceA 和 ServiceB 为例):
-
创建 ServiceA
- 实例化:
new ServiceA() - 提前暴露:将 ObjectFactory 放入三级缓存
- 属性注入:需要注入 ServiceB,触发
getBean("serviceB")
- 实例化:
-
创建 ServiceB
- 实例化:
new ServiceB() - 提前暴露:将 ObjectFactory 放入三级缓存
- 属性注入:需要注入 ServiceA,触发
getBean("serviceA") - 关键:此时从三级缓存中获取 ServiceA 的早期引用
- 将早期引用放入二级缓存,从三级缓存中移除
- 完成 ServiceB 的初始化
- 实例化:
-
完成 ServiceA
- 从二级缓存中获取 ServiceB(已完全初始化)
- 完成 ServiceA 的初始化
- 将 ServiceA 放入一级缓存,从二级缓存中移除
💡 关键理解:循环依赖的解决依赖于"提前暴露"机制。Bean 在完全初始化之前就被放入缓存,供其他 Bean 使用。
⚠️ 限制 :三级缓存机制只适用于单例 Bean 的属性注入方式的循环依赖。构造函数注入的循环依赖无法解决。
4. FactoryBean 机制
核心方法 :getObjectForBeanInstance()
💡 关键理解 :FactoryBean 是一种特殊的 Bean,它本身是一个工厂,用于创建另一个对象。
两种获取方式:
-
获取 FactoryBean 创建的对象 :
getBean("connection")- 去掉
&前缀(如果有) - 检查是否是 FactoryBean
- 调用
factoryBean.getObject()获取对象
- 去掉
-
获取 FactoryBean 本身 :
getBean("&connection")- 保留
&前缀 - 直接返回 FactoryBean 实例
- 保留
🔍 设计思想:
- 工厂模式:FactoryBean 实现了工厂模式,将对象创建的复杂性封装在工厂中
- 统一接口:所有 FactoryBean 都实现同一个接口,便于统一处理
- 特殊标识 :通过
&前缀区分获取工厂本身还是工厂创建的对象
5. MergedBeanDefinition 机制
核心方法 :getMergedBeanDefinition()
💡 关键理解:MergedBeanDefinition 是将子 Bean 定义和父 Bean 定义合并后得到的完整 Bean 定义。
为什么需要合并?
- Bean 定义继承:子 Bean 可以继承父 Bean 的配置
- 属性覆盖:子 Bean 可以覆盖父 Bean 的属性值
- 抽象 Bean:抽象 Bean 不能直接实例化,但可以作为父 Bean 被继承
合并流程:
获取子 Bean 定义
↓
检查是否有 parentBeanName
↓
如果有,递归获取父 Bean 定义(可能来自 parentBeanFactory)
↓
使用 overrideFrom() 合并属性
↓
缓存合并后的 Bean 定义
↓
返回 RootBeanDefinition
🔍 发现:合并机制体现了 Spring 对配置复用的支持,通过继承减少重复配置。
6. BeanAware 回调机制
核心方法 :invokeAwareMethods()
💡 关键理解:Aware 接口允许 Bean 在初始化时获取容器信息,通过回调机制实现。
调用时机:
- 在属性注入之后
- 在初始化方法之前
支持的 Aware 接口:
BeanNameAware:获取 Bean 名称BeanFactoryAware:获取 BeanFactory 容器BeanClassLoaderAware:获取类加载器ApplicationContextAware:获取 ApplicationContext(通过 BeanPostProcessor 处理)
设计思想:
- 回调模式:容器主动通知 Bean,而不是 Bean 主动查询
- 接口隔离:每个 Aware 接口只负责一种信息
- 可选实现:Bean 可以选择性地实现需要的接口
Spring 的设计思想总结
通过整个系列的学习,我们发现了 Spring 框架中几个重要的设计思想:
1. 模板方法模式
doGetBean() 方法定义了 Bean 获取的算法骨架,但每个步骤都可以通过扩展机制增强:
java
protected <T> T doGetBean(...) {
// 步骤1:名称转换(固定)
// 步骤2:缓存查找(固定)
// 步骤3:获取 Bean 定义(可扩展:合并机制)
// 步骤4:检查依赖(可扩展:dependsOn)
// 步骤5:创建 Bean(可扩展:不同作用域)
// 步骤6:处理 FactoryBean(可扩展:特殊处理)
}
💡 关键理解:模板方法模式让 Spring 在保持核心流程不变的同时,提供了丰富的扩展点。
2. 缓存机制
Spring 使用了多级缓存来优化性能:
- Bean 定义缓存:避免重复解析配置
- 单例缓存:避免重复创建单例 Bean
- 早期引用缓存:解决循环依赖问题
🔍 发现:缓存机制体现了 Spring 对性能的重视,通过缓存减少重复计算。
3. 延迟加载机制
Spring 采用了多种延迟加载策略:
- Bean 定义延迟加载:只有在需要时才解析 Bean 定义
- Bean 实例延迟加载 :只有在调用
getBean()时才创建 Bean - 早期引用延迟创建:只有在真正需要时才通过 ObjectFactory 创建
💡 思考:延迟加载让 Spring 能够支持大型应用,避免启动时加载所有 Bean。
4. 扩展机制
Spring 提供了丰富的扩展点:
- BeanPostProcessor:在 Bean 初始化前后进行处理
- BeanFactoryPostProcessor:在 Bean 定义加载后进行处理
- FactoryBean:自定义 Bean 创建逻辑
- Aware 接口:让 Bean 感知容器信息
🔍 发现:扩展机制让 Spring 能够在不修改核心代码的情况下,支持各种功能扩展。
关键方法总结
| 方法 | 作用 | 设计模式 | 所在文章 |
|---|---|---|---|
doGetBean |
Bean 获取的核心方法 | 模板方法模式 | (一) |
transformedBeanName |
名称转换 | - | (二) |
canonicalName |
别名解析 | - | (二) |
getSingleton |
从缓存获取单例 | - | (二)、(三) |
getMergedBeanDefinition |
获取合并后的 Bean 定义 | - | (五) |
createBean |
创建 Bean 实例 | 模板方法模式 | (一) |
doCreateBean |
Bean 创建的核心方法 | 模板方法模式 | (一)、(三) |
populateBean |
属性注入 | - | (三) |
initializeBean |
Bean 初始化 | 模板方法模式 | (六) |
invokeAwareMethods |
调用 Aware 接口 | 策略模式 | (六) |
getObjectForBeanInstance |
处理 FactoryBean | 工厂方法模式 | (四) |
学习要点回顾
通过整个系列的学习,我们应该掌握以下要点:
1. 理解 Bean 获取的完整流程
从 getBean() 到 doGetBean(),再到 createBean() 和 doCreateBean(),理解每个方法的作用和调用关系。
2. 掌握核心机制
- 名称转换:如何处理别名和 FactoryBean 前缀
- 缓存机制:三级缓存的作用和关系
- 循环依赖:如何通过三级缓存解决循环依赖
- FactoryBean:如何区分获取工厂本身和工厂创建的对象
- Bean 定义合并:如何处理父子 Bean 定义的继承
- Aware 接口:如何通过回调机制让 Bean 感知容器信息
3. 理解设计思想
- 模板方法模式:定义算法骨架,提供扩展点
- 缓存机制:优化性能,减少重复计算
- 延迟加载:支持大型应用,避免启动时加载所有 Bean
- 扩展机制:提供丰富的扩展点,支持功能扩展
4. 掌握调试技巧
通过实际的调试过程,理解源码的执行流程,这是深入理解 Spring 的关键。
与后续学习的联系
💡 关键理解 :getBean() 是 Spring IoC 容器的核心方法,理解它有助于理解 Spring 的其他机制:
- Bean 生命周期 :
getBean()触发了 Bean 的完整生命周期 - 依赖注入 :在
populateBean()中完成依赖注入 - AOP 代理:在 BeanPostProcessor 中创建 AOP 代理
- 事务管理:通过 AOP 代理实现事务管理
- Web 上下文:父子容器的机制在 Web 应用中广泛应用
🔍 发现 :Spring 的各个机制是相互关联的,理解 getBean() 是理解其他机制的基础。
总结
通过整个 getBean 源码实战系列的学习,我们深入理解了 Spring IoC 容器的核心机制。从最简单的 Bean 获取,到复杂的循环依赖处理,再到 FactoryBean、MergedBeanDefinition 和 BeanAware 机制,每一步都体现了 Spring 框架的设计智慧。
💡 核心收获:
- 理解了 Bean 获取的完整流程:从名称转换到 Bean 创建,再到初始化
- 掌握了核心机制:三级缓存、循环依赖、FactoryBean、Bean 定义合并、Aware 接口
- 理解了设计思想:模板方法模式、缓存机制、延迟加载、扩展机制
- 掌握了调试技巧:通过实际调试理解源码执行流程
🔍 下一步学习建议:
- BeanPostProcessor:深入理解 Bean 后置处理器的机制
- AOP 机制:理解 AOP 代理的创建过程
- 事务管理:理解 Spring 事务管理的实现原理
- Web 上下文:理解父子容器在 Web 应用中的应用
💡 最后的话:源码学习是一个循序渐进的过程,不要急于求成。通过实际调试、深入思考,才能真正理解 Spring 的设计思想。希望这个系列能够帮助大家更好地理解 Spring 框架!