【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 管理的关键优化

相关推荐
程序员爱钓鱼2 小时前
Node.js 编程实战:RESTful API 设计
前端·后端·node.js
程序员爱钓鱼2 小时前
Node.js 编程实战:GraphQL 简介与实战
前端·后端·node.js
washingtin2 小时前
Get “https://registry-1.docker.io/v2/“: context deadline exceeded
java·开发语言
一路往蓝-Anbo2 小时前
C语言从句柄到对象 (七) —— 给对象加把锁:RTOS 环境下的并发安全
java·c语言·开发语言·stm32·单片机·嵌入式硬件·算法
利刃大大2 小时前
【SpringBoot】validation参数校验 && JWT鉴权实现 && 加密/加盐
java·spring boot·jwt·加密
小北方城市网2 小时前
第 3 课:前后端全栈联动核心 —— 接口规范 + AJAX + 跨域解决(打通前后端壁垒)
java·大数据·网络·python
降临-max3 小时前
JavaWeb企业级开发---MySQL
java·开发语言·数据库·笔记·后端·mysql
C雨后彩虹3 小时前
二维伞的雨滴效应
java·数据结构·算法·华为·面试
oMcLin3 小时前
Ubuntu 22.04 Docker 容器启动失败:解决 Overlay2 存储驱动冲突
java·ubuntu·docker