Spring source code reading 22: Use FactoryBean to create Bean instance objects

This is the 33rd day of my participation in the "Nuggets Daily New Plan·August Update Challenge", click to view the details of the event

Based on Spring Framework v5.2.6.RELEASE

Continued from the previous article: Spring Source Code Reading 21: Circular Dependencies and L3 Cache

Recap

The previous article introduced that in the doGetBeanmethod of AbstractBeanFactory, after completing the bean name conversion, the first step is to obtain the instance object of the singleton bean from the third-level cache of the Spring container.

If the Bean instance object is obtained in the container's cache, Spring will further process this object. The code for this part is as follows:

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);
}
复制代码

In addition to the log processing part, the method is mainly called to getObjectForBeanInstanceobtain the Bean instance that is finally returned as the result.

After obtaining the Bean instance object from the cache, what follow-up work does Spring need to do. This article takes this method as the starting point for in-depth analysis.

Handling different types of beans

First of all, you need to pay attention to the following getObjectForBeanInstanceparameters when calling the method, including the following:

  • The Bean instance object obtained from the cache sharedInstance, according to the previous if judgment, this object is not empty at this time.
  • The parameter of the calling doGetBeanmethod name, that is, the value before the bean name conversion. The value of this is also the parameter value passed in when the method is nameinitially called . It can be said that it represents the intention when calling the method.getBeangetBean
  • The converted canonical beanName, that is, the unique identifier of the Bean in the container.
  • The last parameter is passed in null, and the subsequent analysis finally arrives and then looks at it in detail.

Next enter the code of the method:

// 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属性的值,是不是一个工厂引用,具体的判断方式如下:

// 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;
}
复制代码

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

总结

At this point, the process of obtaining the Bean instance from the cache and getObjectForBeanInstanceobtaining the final result object through the method is finished. getObjectForBeanInstanceThe main function of the method is to process the Bean of the type that implements the FactoryBean interface. Here is a knowledge point that few people know, that is, if the type of a bean is the implementation class of FactoryBean, then, if you want to get the instance of the FactoryBean itself that created the bean instance, you can getBean("&beanName")get it by calling.

After this part of the process, the object obtained here is the object doGetBeanto be returned by the method at the end. However, so far, we have only analyzed doGetBeana small part of the method, that is, the instance object of the Bean can be obtained in the cache. If the doGetBeanBean instance is not obtained in the cache in the first step of the method, then the Bean instance needs to be initialized from scratch. From the next article, we will analyze how the Bean is initialized and obtained in this case. .

Guess you like

Origin juejin.im/post/7136590694340100127