【Spring源码】getBean源码实战(二)

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 会执行以下步骤:

  1. 第一步:通过 id 获取 Bean

    • Bean 被创建并放入一级缓存 singletonObjects
    • 这个过程在第一篇文档中已经详细分析过
  2. 第二步:通过别名获取 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 名称,支持链式别名

处理过程

  1. 第一次查找 :从 aliasMap 中查找 "userServiceAlias",找到对应的值 "userService"
  2. 判断是否为最终 Bean 名称
    • 如果找到的值在 aliasMap 中不存在(即不是别名),说明找到了最终的 Bean 名称
    • 如果找到的值仍然是别名(链式别名),继续循环查找
  3. 链式别名示例
    • 假设 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 实例。

处理逻辑

  1. 调用 getSingleton(beanName)

    • 从单例缓存中获取 Bean 实例
    • 传入转换后的 Bean 名称 "userService"(不是别名 "userServiceAlias"
  2. 判断是否获取成功

    • 如果 sharedInstance != nullargs == 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 名称获取

处理过程

  1. 从一级缓存查找

    • 调用 this.singletonObjects.get(beanName),传入转换后的 Bean 名称 "userService"
    • 由于 Bean 已经在第一次通过 id 获取时被创建并放入一级缓存,所以能够直接获取到实例
    • 注意:这里使用的是转换后的 Bean 名称,而不是原始的别名
  2. 直接返回

    • 如果从一级缓存中获取到了 Bean 实例(singletonObject != null),直接返回
    • 无需进入创建流程,这是与第一篇最大的区别
    • 性能优势:O(1) 时间复杂度的缓存查找,比创建 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;
}

处理逻辑

  1. 判断是否要获取 FactoryBean 本身

    • 如果 name& 开头(如 "&connection"),说明要获取 FactoryBean 本身
    • 返回 FactoryBean 实例,而不是 FactoryBean 创建的对象
  2. 判断是否为 FactoryBean

    • 如果 beanInstance 不是 FactoryBean 类型,直接返回实例
    • 对于 UserService,走这个分支UserService 不是 FactoryBean,直接返回实例
  3. 处理 FactoryBean(本次不关注):

    • 如果 beanInstanceFactoryBean 类型,调用 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 问题分析

问题AbstractAutowireCapableBeanFactoryAbstractBeanFactory 是否是装饰器模式?特别是在 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 模板方法模式

装饰器模式的特征

  • 使用组合关系(装饰器包装被装饰者)
  • 运行时动态添加功能
  • 可以多层装饰

模板方法模式的特征

  • 使用继承关系(子类继承父类)
  • 编译时确定行为
  • 定义算法骨架,子类实现具体步骤

结论

  • AbstractAutowireCapableBeanFactoryAbstractBeanFactory继承关系,不是组合关系
  • 它们使用的是模板方法模式,而不是装饰器模式
  • 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 个步骤:

  1. 别名转换 :将别名 "userServiceAlias" 转换为实际的 Bean 名称 "userService"

    • 处理 FactoryBean 前缀(&
    • 通过 aliasMap 查找别名对应的 Bean 名称(支持链式别名)
  2. 缓存查找 :从一级缓存 singletonObjects 中直接获取 Bean 实例

    • 使用转换后的 Bean 名称 "userService" 从缓存中查找
    • 如果缓存中存在,直接返回实例
  3. 处理 FactoryBean :通过 getObjectForBeanInstance 处理 FactoryBean 的特殊情况

    • 对于普通 Bean(如 UserService),直接返回实例
    • 对于 FactoryBean,需要调用 getObject() 方法获取实际对象

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 关键要点

  1. 一次创建,多次复用

    • Bean 在第一次获取时被创建并放入一级缓存
    • 后续通过任何名称(id 或别名)获取时,都直接从缓存中获取
    • 无需重新创建,大大提高了性能
  2. 别名转换机制

    • Spring 使用 aliasMap 存储别名到 Bean 名称的映射关系
    • 支持链式别名(别名指向别名)
    • 通过 canonicalName() 方法递归查找,直到找到最终的 Bean 名称
  3. 缓存机制的优势

    • 一级缓存 singletonObjects 使用 ConcurrentHashMap 实现,查找效率为 O(1)
    • 对于单例 Bean,只要创建过一次,后续所有获取操作都能命中缓存
    • 这是 Spring 容器高性能的重要保障
  4. 设计模式的应用

    • AbstractBeanFactoryAbstractAutowireCapableBeanFactory 使用模板方法模式
    • 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 管理的关键优化

相关推荐
曹牧10 分钟前
文档格式:OFD
java
豆角焖肉35 分钟前
Maven进阶与搭建私服
java·maven
空中湖36 分钟前
Spring AI Agent 编排:ReAct 模式 + 多 Agent 协作实战
人工智能·spring·react.js
大不点wow1 小时前
Java序列化与反序列化:让对象走出JVM
java·开发语言·jvm
止语Lab1 小时前
好的 DX 不等于少写代码——三种语言的摩擦力设计课
后端
吃饱了得干活1 小时前
别再手动解析 LLM 输出了!LangChain 四种结构化输出方案对比
后端·python·langchain
噢,我明白了1 小时前
Java中日期和字符串的处理
java·开发语言·日期
程序员天天困1 小时前
Arthas trace 命令怎么用?一行定位最慢那行代码
jvm·后端
Huiturn1 小时前
GPT 5.6 连续编码 10 小时,纯 Python 啃下 Word 二进制格式——doc2docx 实现拆解
后端
dkbnull1 小时前
Spring Boot请求处理组件对比详解
java·spring boot