getBean源码实战(二)
代码仓库 :Gitee 仓库链接
本文档的所有示例代码都可以在代码仓库中找到,建议结合代码一起阅读。
1. 使用别名获取已缓存的 Bean
1.1 示例 Bean:UserService
本篇继续使用第一篇中的 UserService 示例,但这次我们关注的是:当 Bean 已经被放入一级缓存后,使用别名获取 Bean 的流程。
Bean 类定义:
1:25:spring-source/code/spring-basic/src/main/java/com/example/basic/UserService.java
package com.example.basic;
/**
* 简单的UserService类,用于演示bean标签的基本属性
*/
public class UserService {
private String name;
public UserService() {
System.out.println("UserService 构造函数被调用");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void sayHello() {
System.out.println("Hello, I am UserService");
}
}
XML 配置:
7:9:spring-source/code/spring-basic/src/main/resources/bean-basic-attributes.xml
<!-- 简单的bean标签示例:包含id、name、class属性 -->
<bean id="userService" name="userServiceAlias,userServiceAlias2" class="com.example.basic.UserService">
</bean>
获取 Bean 的代码:
15:40:spring-source/code/spring-basic/src/test/java/com/example/basic/BeanBasicAttributesTest.java
@Test
public void testBeanWithIdNameClass() {
// 加载XML配置文件
BeanFactory factory = new XmlBeanFactory(
new ClassPathResource("bean-basic-attributes.xml")
);
// 第一步:通过id获取bean(此时bean会被创建并放入一级缓存)
UserService userServiceById = (UserService) factory.getBean("userService");
assertNotNull(userServiceById);
// 第二步:通过name中的第一个别名获取bean(此时bean已在一级缓存中)
UserService userServiceByAlias1 = (UserService) factory.getBean("userServiceAlias");
assertNotNull(userServiceByAlias1);
// 第三步:通过name中的第二个别名获取bean(此时bean已在一级缓存中)
UserService userServiceByAlias2 = (UserService) factory.getBean("userServiceAlias2");
assertNotNull(userServiceByAlias2);
// 验证它们是同一个实例(默认是单例)
assertSame(userServiceById, userServiceByAlias1);
assertSame(userServiceById, userServiceByAlias2);
// 调用方法验证bean正常工作
userServiceById.sayHello();
}
1.2 代码仓库位置
- Bean 类 :
code/spring-basic/src/main/java/com/example/basic/UserService.java - 配置文件 :
code/spring-basic/src/main/resources/bean-basic-attributes.xml - 测试类 :
code/spring-basic/src/test/java/com/example/basic/BeanBasicAttributesTest.java
1.3 执行流程概述
当我们先通过 id 获取 Bean(factory.getBean("userService")),然后再通过别名获取 Bean(factory.getBean("userServiceAlias"))时,Spring 会执行以下步骤:
-
第一步:通过 id 获取 Bean
- Bean 被创建并放入一级缓存
singletonObjects中 - 这个过程在第一篇文档中已经详细分析过
- Bean 被创建并放入一级缓存
-
第二步:通过别名获取 Bean
- 别名转换 :将别名
"userServiceAlias"转换为实际的 Bean 名称"userService" - 缓存查找 :从一级缓存
singletonObjects中直接获取已存在的 Bean 实例 - 返回实例:直接返回缓存的 Bean 实例,无需重新创建
- 别名转换 :将别名
本篇重点分析第二步:当 Bean 已在一级缓存中时,使用别名获取 Bean 的流程。这个过程比创建 Bean 简单得多,主要涉及别名转换和缓存查找。
1.4 深入 doGetBean 方法(别名获取流程)
当我们调用 factory.getBean("userServiceAlias") 时,Spring 会进入 AbstractBeanFactory.doGetBean() 方法。由于此时 userService Bean 已经被创建并放入一级缓存,所以整个流程比第一篇简单得多。
核心流程:
factory.getBean("userServiceAlias")
↓
AbstractBeanFactory.doGetBean("userServiceAlias")
↓
步骤1:transformedBeanName("userServiceAlias") → "userService"
↓
步骤2:getSingleton("userService") → 从一级缓存获取
↓
步骤3:getObjectForBeanInstance(...) → 处理FactoryBean(普通Bean直接返回)
↓
返回 UserService 实例
步骤 1:翻译 Bean 名称
源码位置 :AbstractBeanFactory.doGetBean()
java
// AbstractBeanFactory.java
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType,
@Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
// 步骤1:翻译Bean名称
final String beanName = transformedBeanName(name);
// ... 后续步骤
}
作用 :将传入的别名 "userServiceAlias" 转换为实际的 Bean 名称 "userService"。
transformedBeanName 方法实现:
java
// AbstractBeanFactory.java
protected String transformedBeanName(String name) {
return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}
翻译 Bean 名称分为两个步骤:
步骤 1.1:解析 FactoryBean 的名称(本次不关注)
源码位置 :BeanFactoryUtils.transformedBeanName()
java
// BeanFactoryUtils.java
public static String transformedBeanName(String name) {
// 如果name以&开头,去掉&前缀
// 这是为了获取FactoryBean本身,而不是FactoryBean创建的对象
if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
return name;
}
return name.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
}
作用 :处理 FactoryBean 的特殊前缀 &。
- 如果传入的名称以
&开头(如"&connection"),去掉&前缀,返回"connection" - 这是为了获取 FactoryBean 本身,而不是 FactoryBean 创建的对象
- 本次不关注:对于普通的 Bean 获取,不涉及 FactoryBean 前缀处理
步骤 1.2:通过别名找 Bean 名(支持链式别名)
源码位置 :SimpleAliasRegistry.canonicalName()
java
// SimpleAliasRegistry.java
public String canonicalName(String name) {
String canonicalName = name;
String resolvedName;
do {
// 从aliasMap中查找别名对应的值
resolvedName = this.aliasMap.get(canonicalName);
if (resolvedName != null) {
// 如果找到了,继续查找(支持链式别名)
canonicalName = resolvedName;
}
} while (resolvedName != null); // 循环直到找不到为止
return canonicalName;
}
作用 :通过别名映射表查找实际的 Bean 名称,支持链式别名。
处理过程:
- 第一次查找 :从
aliasMap中查找"userServiceAlias",找到对应的值"userService" - 判断是否为最终 Bean 名称 :
- 如果找到的值在
aliasMap中不存在(即不是别名),说明找到了最终的 Bean 名称 - 如果找到的值仍然是别名(链式别名),继续循环查找
- 如果找到的值在
- 链式别名示例 :
- 假设
aliasMap中存储:"alias1" -> "alias2","alias2" -> "userService" - 当传入
"alias1"时:- 第一次查找:
aliasMap.get("alias1")返回"alias2" - 第二次查找:
aliasMap.get("alias2")返回"userService" - 第三次查找:
aliasMap.get("userService")返回null(不是别名) - 返回最终的 Bean 名称:
"userService"
- 第一次查找:
- 假设
别名映射关系(本次示例):
aliasMap中存储:"userServiceAlias" -> "userService"aliasMap中存储:"userServiceAlias2" -> "userService"
链式别名示例(说明支持,但本次不涉及):
aliasMap中存储:"alias1" -> "alias2","alias2" -> "userService"- 通过
"alias1"可以找到"userService"
关键点 :canonicalName() 方法使用 do-while 循环,支持链式别名查找,直到找到最终的 Bean 名称为止。
步骤 2:通过一级缓存得到 Bean 对象
源码位置 :AbstractBeanFactory.doGetBean()
java
// AbstractBeanFactory.java
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType,
@Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
final String beanName = transformedBeanName(name); // "userService"
// 步骤2:通过一级缓存得到Bean对象
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
return bean;
}
// ... 如果缓存中没有,进入创建流程(本次不关注)
}
作用 :从一级缓存 singletonObjects 中获取已存在的 Bean 实例。
处理逻辑:
-
调用
getSingleton(beanName):- 从单例缓存中获取 Bean 实例
- 传入转换后的 Bean 名称
"userService"(不是别名"userServiceAlias")
-
判断是否获取成功:
- 如果
sharedInstance != null且args == null,说明从缓存中获取到了 Bean 实例 - 调用
getObjectForBeanInstance()处理 FactoryBean 的情况 - 直接返回 Bean 实例,无需进入创建流程
- 如果
getSingleton 方法实现:
java
// DefaultSingletonBeanRegistry.java
public Object getSingleton(String beanName) {
return getSingleton(beanName, true);
}
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 第一级缓存:完全初始化好的单例Bean
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject != null) {
return singletonObject; // 直接返回,无需后续处理
}
// 如果一级缓存中没有,继续查找二级和三级缓存(本次不关注)
// 这是为了处理循环依赖的情况
// if (isSingletonCurrentlyInCreation(beanName)) {
// // 从二级缓存earlySingletonObjects中查找
// // 从三级缓存singletonFactories中查找
// }
return null;
}
一级缓存 singletonObjects:
java
// DefaultSingletonBeanRegistry.java
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
特点:
- 存储内容:完全初始化好的单例 Bean 实例
- 线程安全 :使用
ConcurrentHashMap实现 - 查找效率:O(1) 时间复杂度,直接通过 Bean 名称获取
处理过程:
-
从一级缓存查找:
- 调用
this.singletonObjects.get(beanName),传入转换后的 Bean 名称"userService" - 由于 Bean 已经在第一次通过 id 获取时被创建并放入一级缓存,所以能够直接获取到实例
- 注意:这里使用的是转换后的 Bean 名称,而不是原始的别名
- 调用
-
直接返回:
- 如果从一级缓存中获取到了 Bean 实例(
singletonObject != null),直接返回 - 无需进入创建流程,这是与第一篇最大的区别
- 性能优势:O(1) 时间复杂度的缓存查找,比创建 Bean 快得多
- 如果从一级缓存中获取到了 Bean 实例(
与第一篇的区别:
| 对比项 | 第一篇(首次获取) | 第二篇(别名获取已缓存) |
|---|---|---|
| 缓存状态 | 一级缓存中没有 | 一级缓存中已有 |
| 执行流程 | 进入完整的创建流程(30 个步骤) | 直接从缓存获取(2 个步骤) |
| 性能 | 需要创建 Bean 实例,耗时较长 | 直接从缓存获取,性能极佳 |
| 调用方法 | getSingleton(beanName, singletonFactory) |
getSingleton(beanName) |
关键点:
-
一次创建,多次复用:
- Bean 在第一次获取时被创建并放入一级缓存
- 后续通过任何名称(id 或别名)获取时,都直接从缓存中获取
- 无需重新创建,大大提高了性能
-
别名转换后查找:
- 虽然传入的是别名
"userServiceAlias",但经过别名转换后,使用实际的 Bean 名称"userService"从缓存中查找 - 这是 Spring 别名机制的核心:别名只是名称的映射,实际的 Bean 实例存储在缓存中,使用 Bean 名称作为 key
- 虽然传入的是别名
-
缓存命中率高:
- 对于单例 Bean,只要创建过一次,后续所有获取操作都能命中缓存
- 无论是通过 id 还是别名获取,都能从同一个缓存中获取到同一个实例
步骤 3:通过 getObjectForBeanInstance 获取 Bean 实例
源码位置 :AbstractBeanFactory.doGetBean()
java
// AbstractBeanFactory.java
protected <T> T doGetBean(...) {
final String beanName = transformedBeanName(name); // "userService"
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
// 步骤3:通过getObjectForBeanInstance获取Bean实例
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
return bean;
}
// ... 后续步骤
}
作用:处理 FactoryBean 的特殊情况,对于普通 Bean 直接返回实例。
getObjectForBeanInstance 方法实现:
java
// AbstractBeanFactory.java
protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
// 如果name以&开头,说明要获取FactoryBean本身
if (BeanFactoryUtils.isFactoryDereference(name)) {
if (beanInstance instanceof NullBean) {
return beanInstance;
}
if (!(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
}
if (mbd != null) {
mbd.isFactoryBean = true;
}
return beanInstance; // 返回FactoryBean本身
}
// 如果beanInstance不是FactoryBean,直接返回
if (!(beanInstance instanceof FactoryBean)) {
return beanInstance; // 对于UserService,走这个分支
}
// 如果beanInstance是FactoryBean,需要调用getObject()方法获取实际对象
Object object = null;
if (mbd != null) {
mbd.isFactoryBean = true;
}
else {
object = getCachedObjectForFactoryBean(beanName);
}
if (object == null) {
FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
if (mbd == null && containsBeanDefinition(beanName)) {
mbd = getMergedLocalBeanDefinition(beanName);
}
boolean synthetic = (mbd != null && mbd.isSynthetic());
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
}
处理逻辑:
-
判断是否要获取 FactoryBean 本身:
- 如果
name以&开头(如"&connection"),说明要获取 FactoryBean 本身 - 返回 FactoryBean 实例,而不是 FactoryBean 创建的对象
- 如果
-
判断是否为 FactoryBean:
- 如果
beanInstance不是FactoryBean类型,直接返回实例 - 对于
UserService,走这个分支 :UserService不是FactoryBean,直接返回实例
- 如果
-
处理 FactoryBean(本次不关注):
- 如果
beanInstance是FactoryBean类型,调用getObject()方法获取实际对象 - 这是 FactoryBean 的核心机制:容器中存储的是 FactoryBean 实例,但获取时返回的是 FactoryBean 创建的对象
- 如果
对于 UserService 的处理:
java
// 对于UserService,不是FactoryBean,直接返回
if (!(beanInstance instanceof FactoryBean)) {
return beanInstance; // 直接返回UserService实例
}
UserService不是FactoryBean类型- 直接返回从一级缓存中获取的
UserService实例 - 无需任何额外处理
FactoryBean 的处理示例(说明机制,本次不关注):
假设有一个 ConnectionFactoryBean 实现了 FactoryBean<Connection>:
java
// 配置:<bean id="connection" class="ConnectionFactoryBean">
// 通过 "connection" 获取:返回 Connection 对象(FactoryBean.getObject()的结果)
Connection conn = factory.getBean("connection", Connection.class);
// 通过 "&connection" 获取:返回 ConnectionFactoryBean 本身
FactoryBean<?> factoryBean = factory.getBean("&connection", FactoryBean.class);
关键点:
- 普通 Bean:直接返回实例,无需额外处理
- FactoryBean:需要区分获取 FactoryBean 本身还是 FactoryBean 创建的对象
&前缀:用于获取 FactoryBean 本身,而不是 FactoryBean 创建的对象- 本次不关注 :
UserService是普通 Bean,不涉及 FactoryBean 的处理逻辑
3. 设计模式说明:AbstractAutowireCapableBeanFactory 和 AbstractBeanFactory
3.1 问题分析
问题 :AbstractAutowireCapableBeanFactory 和 AbstractBeanFactory 是否是装饰器模式?特别是在 getObjectForBeanInstance 方法上?
答案 :不是装饰器模式,而是模板方法模式。
3.2 继承关系
java
// AbstractBeanFactory:抽象基类
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport
implements ConfigurableBeanFactory {
// 定义获取Bean的通用流程(模板方法)
protected <T> T doGetBean(...) {
// 1. 翻译Bean名称
// 2. 从缓存获取
// 3. 调用getObjectForBeanInstance处理FactoryBean
// 4. 如果缓存中没有,调用createBean创建(由子类实现)
}
// 处理FactoryBean的方法
protected Object getObjectForBeanInstance(...) {
// 处理FactoryBean的逻辑
}
}
// AbstractAutowireCapableBeanFactory:具体实现类
public abstract class AbstractAutowireCapableBeanFactory
extends AbstractBeanFactory implements AutowireCapableBeanFactory {
// 实现具体的Bean创建逻辑
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, ...) {
// 实现Bean的创建、属性填充、初始化等逻辑
}
}
3.3 模板方法模式的特征
模板方法模式:
- 在抽象类中定义算法的骨架(模板方法)
- 将一些步骤的具体实现延迟到子类
- 子类可以重写某些步骤,但不能改变算法的整体结构
在 Spring 中的应用:
AbstractBeanFactory.doGetBean()定义了获取 Bean 的通用流程(模板方法)AbstractAutowireCapableBeanFactory.createBean()实现了具体的 Bean 创建逻辑(具体步骤)
3.4 装饰器模式 vs 模板方法模式
装饰器模式的特征:
- 使用组合关系(装饰器包装被装饰者)
- 运行时动态添加功能
- 可以多层装饰
模板方法模式的特征:
- 使用继承关系(子类继承父类)
- 编译时确定行为
- 定义算法骨架,子类实现具体步骤
结论:
AbstractAutowireCapableBeanFactory和AbstractBeanFactory是继承关系,不是组合关系- 它们使用的是模板方法模式,而不是装饰器模式
getObjectForBeanInstance方法在AbstractBeanFactory中定义,用于处理 FactoryBean,这是工厂方法模式的应用
3.5 getObjectForBeanInstance 方法的位置
getObjectForBeanInstance 方法定义在 AbstractBeanFactory 中,用于处理 FactoryBean 的特殊情况:
java
// AbstractBeanFactory.java
protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
// 处理FactoryBean的逻辑
// 这个方法在AbstractBeanFactory中定义,子类可以重写,但通常不需要
}
说明:
- 该方法在
AbstractBeanFactory中已经完整实现 AbstractAutowireCapableBeanFactory没有重写这个方法- 这不是装饰器模式,而是模板方法模式中父类提供的通用方法
2. 总结
2.1 流程总结
通过别名获取已缓存的 Bean 的流程非常简单,只需要 3 个步骤:
-
别名转换 :将别名
"userServiceAlias"转换为实际的 Bean 名称"userService"- 处理 FactoryBean 前缀(
&) - 通过
aliasMap查找别名对应的 Bean 名称(支持链式别名)
- 处理 FactoryBean 前缀(
-
缓存查找 :从一级缓存
singletonObjects中直接获取 Bean 实例- 使用转换后的 Bean 名称
"userService"从缓存中查找 - 如果缓存中存在,直接返回实例
- 使用转换后的 Bean 名称
-
处理 FactoryBean :通过
getObjectForBeanInstance处理 FactoryBean 的特殊情况- 对于普通 Bean(如
UserService),直接返回实例 - 对于 FactoryBean,需要调用
getObject()方法获取实际对象
- 对于普通 Bean(如
2.2 与第一篇的对比
| 对比项 | 第一篇(首次获取) | 第二篇(别名获取已缓存) |
|---|---|---|
| 输入 | Bean 的 id 或别名 | Bean 的别名 |
| 缓存状态 | 一级缓存中没有 | 一级缓存中已有 |
| 执行步骤 | 30 个步骤(完整创建流程) | 3 个步骤(别名转换 + 缓存查找 + FactoryBean处理) |
| 性能 | 需要创建 Bean 实例,耗时较长 | 直接从缓存获取,性能极佳(O(1)) |
| 调用方法 | getSingleton(beanName, singletonFactory) |
getSingleton(beanName) |
| 核心方法 | doCreateBean() → createBeanInstance() → populateBean() → initializeBean() |
transformedBeanName() → getSingleton() → getObjectForBeanInstance() |
2.3 关键要点
-
一次创建,多次复用:
- Bean 在第一次获取时被创建并放入一级缓存
- 后续通过任何名称(id 或别名)获取时,都直接从缓存中获取
- 无需重新创建,大大提高了性能
-
别名转换机制:
- Spring 使用
aliasMap存储别名到 Bean 名称的映射关系 - 支持链式别名(别名指向别名)
- 通过
canonicalName()方法递归查找,直到找到最终的 Bean 名称
- Spring 使用
-
缓存机制的优势:
- 一级缓存
singletonObjects使用ConcurrentHashMap实现,查找效率为 O(1) - 对于单例 Bean,只要创建过一次,后续所有获取操作都能命中缓存
- 这是 Spring 容器高性能的重要保障
- 一级缓存
-
设计模式的应用:
AbstractBeanFactory和AbstractAutowireCapableBeanFactory使用模板方法模式getObjectForBeanInstance方法体现了工厂方法模式的应用(处理 FactoryBean)- 不是装饰器模式,而是通过继承关系实现功能扩展
2.4 性能分析
时间复杂度:
- 别名转换:O(n),n 为链式别名的长度(通常为 1,即 O(1))
- 缓存查找:O(1),
ConcurrentHashMap.get()操作 - FactoryBean 处理:O(1),类型判断和直接返回
总体时间复杂度:O(1),非常高效
空间复杂度:
- 别名映射表:O(m),m 为别名数量
- 一级缓存:O(k),k 为单例 Bean 数量
实际性能:
- 从缓存获取 Bean 的性能远高于创建 Bean 的性能
- 这是 Spring 容器能够支持大量 Bean 管理的关键优化