Spring 源码阅读 20:获取 Bean 的规范名称
基于 Spring Framework v5.2.6.RELEASE
前情提要
上一篇粗略分析了 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