likes
comments
collection
share

Spring 源码阅读 20:获取 Bean 的规范名称

作者站长头像
站长
· 阅读数 36

基于 Spring Framework v5.2.6.RELEASE

接上篇:Spring 源码阅读 19:如何 get 到一个 Bean?

前情提要

上一篇粗略分析了 AbstractBeanFactory 中doGetBean方法的整体流程,这篇开始深入分析每个环节的源码和原理。首先看doGetBean方法的第一行代码:

final String beanName = transformedBeanName(name);

转换 Bean 的名称

从这个方法的方法名可以看出,这个方法是将doGetBean方法传入的name参数,通过一个转换操作得到了beanName,并且在doGetBean方法后续流程中,大部分需要用到 Bean 名称作为参数的方法调用,都用了这里转换的到的beanName,因此有必要了解一下,这里的 Bean 名称转换到底做了什么。直接看源码:

// org.springframework.beans.factory.support.AbstractBeanFactory#transformedBeanName
/**
* Return the bean name, stripping out the factory dereference prefix if necessary,
* and resolving aliases to canonical names.
*/
protected String transformedBeanName(String name) {
   return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}

这里保留了源码中的部分注释,从中可以看到,这里大概做了两件事。

  • 有必要的话,去掉名称中的工厂逆向引用前缀
  • 处理 Bean 的别名。

也就是说,这里要把getBean方法调用时提供的名称,转换成一个更加规范的beanName。下面看看具体是如何处理的。

去除工厂引用前缀

先看BeanFactoryUtils.transformedBeanName这个方法。

// org.springframework.beans.factory.BeanFactoryUtils#transformedBeanName
public static String transformedBeanName(String name) {
   Assert.notNull(name, "'name' must not be null");
   if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
      return name;
   }
   return transformedBeanNameCache.computeIfAbsent(name, beanName -> {
      do {
         beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
      }
      while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
      return beanName;
   });
}

FACTORY_BEAN_PREFIX其实就是一个&符号。以上代码的逻辑很简单,就是把name开头所有的&都去除掉,并返回余下的部分。关于这个&符号是什么,我们稍后介绍。

别名处理

接下来看canonicalName方法。

// org.springframework.core.SimpleAliasRegistry#canonicalName
public String canonicalName(String name) {
   String canonicalName = name;
   // Handle aliasing...
   String resolvedName;
   do {
      resolvedName = this.aliasMap.get(canonicalName);
      if (resolvedName != null) {
         canonicalName = resolvedName;
      }
   }
   while (resolvedName != null);
   return canonicalName;
}

这里也比较简单,如果调用方法是,参数name是一个Bean 的别名,那么就把它的实际beanName找到并返回。

综合以上两步,可以理解为,这里通过transformedBeanName得到的是 Bean 的实际beanName,也就相当于配置文件中bean标签的id属性的值。这也是后续流程中都使用这个转换后的beanName的原因。

FactoryBean

这里以前面那个&符号的疑问为切入点,再介绍一下 FactoryBean。注意,是 FactoryBean,不是 BeanFactory。

FactoryBean 是 Spring 中一个重要的接口,以下是它的接口定义。

public interface FactoryBean<T> {

String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";

@Nullable
   T getObject() throws Exception;

@Nullable
   Class<?> getObjectType();

default boolean isSingleton() {
      return true;
   }

}

简而言之,实现了 FactoryBean 接口的类,作为 Spring 中 Bean 的类型时,可以自定义其创建的逻辑。这个逻辑在getObject方法中实现,最终返回 Bean 实例对象。因此这里的getObject方法也是 FactoryBean 最重要的方法。

举个例子,有以下的 FactoryBean 的实现类:

public class MyFactoryBean implements FactoryBean<User> {
    public Course getObject() throws Exception {
        User user = new User();
        user.setName("Pseudocode");
        return user;
    }

    public Class<?> getObjectType() {
        return User.class;
    }
}

以及以下的 XML 配置:

<bean id="user" class="xxx.MyFactoryBean"/>

此时,当第一次使用getBean("user")从 Spring 容器中获取 Bean,那么,Spring 会调用 MyFactoryBean 的getObject方法,获得方法体重创建的 User 类型 Bean 实例。但是在调用getObject方法之前,Spring 需要创建一个 MyFactoryBean 类型的对象,这个对象也会在 Spring 容器中作为一个工厂 Bean 实例,如果想要从容器中获取这个工厂 Bean 的实例对象,就可以通过getBean("&user")获取。这里的&就是一个逆向引用的符号,即通过beanName&前缀得到相应的工厂 Bean 的引用。

总结

这篇主要介绍了doGetBean方法的第一步,就是通过传入的名称name获取到规范的名称beanName,主要是去除name中的&前缀,并处理了别名。后半部分还顺便介绍了 FactoryBean,在后续的流程中,FactoryBean 还会频繁地出现。

结合两部分内容可以知道,代码中的name可能是 Bean 的规范名称,也可能是别名,还可能是对应的 FactoryBean 的逆向引用。经过处理后的beanName则就是 Bean 的唯一规范名称。

下一篇会分析接下来的步骤,也就是从单例缓存中获取 Bean 的实例,这里主要是针对 Bean 已经被 Spring 实例化的情况,还会涉及到 Spring 中非常重要的循环依赖问题,敬请期待。

转载自:https://juejin.cn/post/7136231363321004046
评论
请登录