Spring 源码阅读 22:使用 FactoryBean 创建 Bean 实例对象

这是我参与「掘金日新计划 · 8 月更文挑战」的第33天,点击查看活动详情

基于 Spring Framework v5.2.6.RELEASE

接上篇:Spring 源码阅读 21:循环依赖和三级缓存

前情提要

之前的文章介绍了,在 AbstractBeanFactory 的doGetBean方法中,完成 Bean 名称转换后,第一步就是到 Spring 容器的三级缓存中获取单例 Bean 的实例对象。

如果在容器的缓存中获取到 Bean 的实例对象,Spring 还会对这个对象进行进一步处理。这部分的代码如下:

if (sharedInstance != null && args == null) {
   if (logger.isTraceEnabled()) {
      if (isSingletonCurrentlyInCreation(beanName)) {
         logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
               "' that is not fully initialized yet - a consequence of a circular reference");
      }
      else {
         logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
      }
   }
   bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
复制代码

除了日志处理的部分,主要是调用了getObjectForBeanInstance方法获取到了最终作为结果返回的 Bean 实例。

在从缓存中获取到 Bean 的实例对象之后,Spring 还需要做什么后续工作,本文就以这个方法为切入点,进行深入分析。

处理不同类型的 Bean

首先,需要注意以下调用getObjectForBeanInstance方法时的参数,这里包括以下内容:

  • 从缓存中获取到的 Bean 实例对象sharedInstance,根据前面的if判断,此时这个对象不为空。
  • 调用doGetBean方法的参数name,也就是进行 Bean 名称转换之前的值。这个name的值也是最开始调用getBean方法时传入的参数值,可以说,它代表了调用getBean方法时的意图。
  • 转换之后的规范的beanName,也就是 Bean 在容器中的唯一标识符。
  • 最后一个参数传入了null,之后的分析终于到了再具体看。

接下来进入方法的代码:

// org.springframework.beans.factory.support.AbstractBeanFactory#getObjectForBeanInstance
protected Object getObjectForBeanInstance(
      Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {

   // Don't let calling code try to dereference the factory if the bean isn't a factory.
   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;
   }

   // Now we have the bean instance, which may be a normal bean or a FactoryBean.
   // If it's a FactoryBean, we use it to create a bean instance, unless the
   // caller actually wants a reference to the factory.
   if (!(beanInstance instanceof FactoryBean)) {
      return beanInstance;
   }

   Object object = null;
   if (mbd != null) {
      mbd.isFactoryBean = true;
   }
   else {
      object = getCachedObjectForFactoryBean(beanName);
   }
   if (object == null) {
      // Return bean instance from factory.
      FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
      // Caches object obtained from FactoryBean if it is a singleton.
      if (mbd == null && containsBeanDefinition(beanName)) {
         mbd = getMergedLocalBeanDefinition(beanName);
      }
      boolean synthetic = (mbd != null && mbd.isSynthetic());
      object = getObjectFromFactoryBean(factory, beanName, !synthetic);
   }
   return object;
}
复制代码

方法体中的代码,大概用空行分割成了三个部分,我们逐个来分析。

首先判断name属性的值,是不是一个工厂引用,具体的判断方式如下:

扫描二维码关注公众号,回复: 14503956 查看本文章
// org.springframework.beans.factory.BeanFactoryUtils#isFactoryDereference
public static boolean isFactoryDereference(@Nullable String name) {
   return (name != null && name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
}
复制代码

很简单,就是看它是不是以&符号开头的。再次提示,这里判断的是原始的name参数值。

此处需要再说明一下,前文中说,这个值代表了最开始调用getBean方法的意图,什么意思呢?

因为&代表这个name是一个逆向引用,如果调用getBean方法传入的name是以&开头的话,说明调用getBean方法的意图,是为了获取用来创建 Bean 的 FactoryBean 的实例,如果不是以&开头的话,说明调用getBean方法的意图,是为了获取 Bean 的实例对象本身。

上面一段话读三遍,后面要考。

回到代码第一部分的逻辑,如果name是以&符号开头,表明我们要获取的是 FactoryBean 本身的实例。首先判断了,如果beanInstance是一个 NullBean 就直接返回。之后,判断了如果它不是 FactoryBean 的实例,就会报错,没问题的话,则返回。

这里报错是因为,缓存中获取的实例beanInstance不是一个 FactoryBean 的实例,name中包含了&符号代表了方法调用的意图是要获取 FactoryBean 本身的实例。

如果name不是以&符号开头,那么,则说明要获取的就是 Bean 的实例对象。

下面看方法体中的第二部分代码:

if (!(beanInstance instanceof FactoryBean)) {
    return beanInstance;
}
复制代码

这里很简单,如果beanInstance不是 FactoryBean 的实例,那么,它就是要获取的对象实例本身,所以直接返回就可以了。

之后就剩下最后一种情况,beanInstance是 FactoryBean 的实例,且name不以&开头。也就是说,当前已经获取到的beanInstance是创建 Bean 的工厂实例,但是要获取的是工厂创建出来的 Bean 对象实例。

以下是处理这种情况的代码:

Object object = null;
if (mbd != null) {
   mbd.isFactoryBean = true;
}
else {
   object = getCachedObjectForFactoryBean(beanName);
}
if (object == null) {
   // Return bean instance from factory.
   FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
   // Caches object obtained from FactoryBean if it is a singleton.
   if (mbd == null && containsBeanDefinition(beanName)) {
      mbd = getMergedLocalBeanDefinition(beanName);
   }
   boolean synthetic = (mbd != null && mbd.isSynthetic());
   object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
复制代码

根据调用方法时的参数值,这里的mbd一开始是null。因此,首先会通过getCachedObjectForFactoryBean方法获取。

// org.springframework.beans.factory.support.FactoryBeanRegistrySupport#getCachedObjectForFactoryBean
@Nullable
protected Object getCachedObjectForFactoryBean(String beanName) {
   return this.factoryBeanObjectCache.get(beanName);
}
复制代码

这里又出现了一个容器,我们看一下它的定义:

 /** Cache of singleton objects created by FactoryBeans: FactoryBean name to object. */
private final Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<>(16);
复制代码

根据注释可知,它用来缓存被 FactoryBean 创建好的单例对象。

如果从这个容器中获取到的对象不为空,则它就是最后的结果。如果为空,那么进入下一个if语句块。在这个语句块中,首先将beanInstance强制转换为 FactoryBean 的类型,然后判断容器中是否有beanName对应的 BeanDefinition,有的话将其获取到。

这里我们假设 Spring 的配置及初始化过程都没问题,并且这里请求的是一个已经配置的 Bean,那么,这里的mbd变量就是已经获取到的 BeanDinifition。之后的工作,就是开始使用这个factory,来得到真正的 Bean 实例对象,这部分逻辑在getObjectFromFactoryBean方法中。

在分析它之前,顺便说一下传入的参数!synthetic。根据我们的假设,这里的mbd不为空,且synthetic的默认值是false,因此这里传入的参数值是true

使用 FactoryBean 获取 Bean 的对象实例

进入getObjectFromFactoryBean方法的代码:

// org.springframework.beans.factory.support.FactoryBeanRegistrySupport#getObjectFromFactoryBean
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
   if (factory.isSingleton() && containsSingleton(beanName)) {
      synchronized (getSingletonMutex()) {
         Object object = this.factoryBeanObjectCache.get(beanName);
         if (object == null) {
            object = doGetObjectFromFactoryBean(factory, beanName);
            // Only post-process and store if not put there already during getObject() call above
            // (e.g. because of circular reference processing triggered by custom getBean calls)
            Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
            if (alreadyThere != null) {
               object = alreadyThere;
            }
            else {
               if (shouldPostProcess) {
                  if (isSingletonCurrentlyInCreation(beanName)) {
                     // Temporarily return non-post-processed object, not storing it yet..
                     return object;
                  }
                  beforeSingletonCreation(beanName);
                  try {
                     object = postProcessObjectFromFactoryBean(object, beanName);
                  }
                  catch (Throwable ex) {
                     throw new BeanCreationException(beanName,
                           "Post-processing of FactoryBean's singleton object failed", ex);
                  }
                  finally {
                     afterSingletonCreation(beanName);
                  }
               }
               if (containsSingleton(beanName)) {
                  this.factoryBeanObjectCache.put(beanName, object);
               }
            }
         }
         return object;
      }
   }
   else {
      Object object = doGetObjectFromFactoryBean(factory, beanName);
      if (shouldPostProcess) {
         try {
            object = postProcessObjectFromFactoryBean(object, beanName);
         }
         catch (Throwable ex) {
            throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
         }
      }
      return object;
   }
}
复制代码

方法体比较长,我们挑关键的代码来看。

首先判断了factory是不是单例的,且单例缓存中是不是存在beanName对应的单例对象。我们只考虑单例的情况,这里判断条件的结果是true

接下来,会调用doGetObjectFromFactoryBean方法,来创建 Bean 实例对象。这个方法中主要的代码就是调用了factorygetObject方法,这是 FactoryBean 的实现类用来创建对象的方法。创建完之后,进入下面的流程。

判断条件if (shouldPostProcess)中,shouldPostProcess是通过参数传递的,前面已经分析过了它的值是true,因此,进入if语句块的流程。

if语句块中,通过isSingletonCurrentlyInCreation方法判断了实例是不是正在创建中,如果是,就直接返回。这里的判断,其实也是判断一个集合中是否包含beanName,这个集合是singletonsCurrentlyInCreation。假设我们是第一次创建这个实例,则会进入之后的流程,关键的代码有三行:

beforeSingletonCreation(beanName);
object = postProcessObjectFromFactoryBean(object, beanName);
afterSingletonCreation(beanName);
复制代码

这里beforeSingletonCreationafterSingletonCreation方法的源码如下:

// org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#beforeSingletonCreation
protected void beforeSingletonCreation(String beanName) {
   if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
      throw new BeanCurrentlyInCreationException(beanName);
   }
}

// org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#afterSingletonCreation
protected void afterSingletonCreation(String beanName) {
   if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
      throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
   }
}
复制代码

其实就是在执行postProcessObjectFromFactoryBean方法期间,将beanName保存在singletonsCurrentlyInCreation集合中,与前面的判断语句对应。(另外,这里还有一个排除检查的列表inCreationCheckExclusions

最后再看一下postProcessObjectFromFactoryBean方法究竟对object做了什么处理。

protected Object postProcessObjectFromFactoryBean(Object object, String beanName) throws BeansException {
   return object;
}
复制代码

其实什么也没做,因此这里应该是提供给子类的扩展点。

总结

到这里,从缓存中获取到 Bean 实例并通过getObjectForBeanInstance方法获取最后的结果对象的过程就分析完了,getObjectForBeanInstance方法的主要作用,是针对实现了 FactoryBean 接口的类型的 Bean 的处理。这里有一个少有人知道的知识点,就是,如果一个 Bean 的类型是 FactoryBean 的实现类,那么,如果想获取到创建 Bean 实例的 FactoryBean 本身的实例,可以通过调用getBean("&beanName")来获取。

走完这部分的流程,这里得到的对象,就是doGetBean方法最后要返回的对象。不过到此,我们只分析了doGetBean方法的一小部分,也就是 Bean 的实例对象能够在缓存中获取的情况。如果在doGetBean方法的第一步,没有在缓存中获取到 Bean 的实例,那么就需要从头初始化这个 Bean 的实例,从下一篇开始,会分析这种情况下,Bean 是如何被初始化并获取到的。

猜你喜欢

转载自juejin.im/post/7136590694340100127