面试官:Spring 的 getBean 中,transformedBeanName 的作用是什么?

102 阅读3分钟

 

Spring 框架中,getBean 方法是获取 Bean 实例的核心入口。但你是否想过,当传入的 Bean 名称包含别名、特殊符号(比如 &)时,Spring 是如何准确找到目标 Bean 的?这背后的秘密就藏在 transformedBeanName 方法中。本文从源码层面解析 transformedBeanName 的作用,并带你走一遍 Bean 获取的全流程!


一、transformedBeanName 是做什么的?

一句话总结transformedBeanName 的核心作用是对传入的 Bean 名称进行规范化处理,确保后续流程能正确找到目标 Bean。具体来说,它主要解决两类问题:

  1. 处理别名:将别名转换为真实的 Bean 名称。
  2. 处理工厂前缀:去掉 & 符号(获取 FactoryBean 本身时需要用到)。

举个例子:

  • 如果传入的 Bean 名称是 myBeanAlias(别名),transformedBeanName 会将其转换为真实的 Bean 名称(如 myBean)。
  • 如果传入的 Bean 名称是 &myFactoryBeantransformedBeanName 会去掉 &,返回 myFactoryBean,以便后续获取 FactoryBean 实例。

二、从源码看 transformedBeanName 的实现

Spring 源码中,transformedBeanName 方法位于 AbstractBeanFactory 类中。以下是关键代码片段:

protected String transformedBeanName(String name) {
    // 1. 处理别名:将别名转换为真实名称
    String beanName = canonicalName(name);
    // 2. 处理工厂前缀:去掉开头的 "&"
    return (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX) ? 
            beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length()) : beanName;
}

步骤解析

  1. 处理别名:通过 canonicalName 方法,将别名转换为注册的真实 Bean 名称。
  2. 处理工厂前缀:如果 Bean 名称以 & 开头(例如 &myFactoryBean),则去掉 &,返回真正的 Bean 名称(myFactoryBean)。

三、Bean 的获取流程全解析

理解了 transformedBeanName 的作用后,我们再来看 getBean 方法的整体流程。以 getBean(String name) 为例:

1. 调用 getBean 方法

public Object getBean(String name) throws BeansException {
    return doGetBean(name, null, null, false);
}

2. 进入 doGetBean 方法

在 doGetBean 方法中,第一步就是调用 transformedBeanName

protected <T> T doGetBean(String name, ...) {
    // 关键步骤1:转换 Bean 名称
    String beanName = transformedBeanName(name);
    // 关键步骤2:尝试从缓存获取 Bean
    Object sharedInstance = getSingleton(beanName);
    // 缓存未命中,则创建 Bean
    if (sharedInstance == null) {
        // 创建 Bean 的流程...
    }
    return (T) beanInstance;
}

3. 流程总结

复制

1. 转换名称 → 2. 查缓存 → 3. 缓存命中则返回 → 4. 缓存未命中则创建 Bean

四、transformedBeanName 的实际场景

场景1:处理别名

假设定义了一个别名:

<bean id="myBean" class="com.example.MyBean"/>
<alias name="myBean" alias="myBeanAlias"/>

运行 HTML

调用 getBean("myBeanAlias") 时:

  • transformedBeanName 会将 myBeanAlias 转换为 myBean
  • 后续流程直接根据 myBean 查找 Bean。

场景2:获取 FactoryBean 本身

假设有一个 FactoryBean:

@Component("myFactoryBean")
public class MyFactoryBean implements FactoryBean<Object> { ... }

调用 getBean("&myFactoryBean") 时:

  • transformedBeanName 会去掉 &,得到 myFactoryBean
  • 后续流程会返回 MyFactoryBean 的实例(而不是它生产的对象)。

五、为什么需要处理工厂前缀?

FactoryBean 是 Spring 中用于生产 Bean 的特殊接口。默认情况下,getBean("myFactoryBean") 返回的是 FactoryBean 生产的对象,而非 FactoryBean 本身。如果用户想获取 FactoryBean 实例,必须在名称前加 &。例如:

// 获取 FactoryBean 生产的对象
Object product = context.getBean("myFactoryBean");
// 获取 FactoryBean 本身
FactoryBean<?> factory = context.getBean("&myFactoryBean");

transformedBeanName 的作用就是去掉 &,让后续流程能正确找到 FactoryBean 的实例。


六、Bean 获取流程中的其他关键步骤

除了 transformedBeanNamedoGetBean 方法还包含以下核心逻辑:

  1. 检查缓存:优先从单例缓存(singletonObjects)中获取 Bean,避免重复创建。
  2. 处理原型作用域 Bean:如果 Bean 是 prototype,每次都会创建新实例。
  3. 解决依赖循环:通过三级缓存提前暴露半成品 Bean。
  4. 初始化 Bean:调用 @PostConstructInitializingBean 等生命周期方法。

七、总结

  • transformedBeanName 的作用:规范化 Bean 名称,处理别名和工厂前缀。

  • 核心流程getBean → transformedBeanName → 查缓存 → 创建 Bean。

  • 注意事项

    • 使用别名时,确保别名已正确注册。
    • 获取 FactoryBean 实例时,名称前需加 &