Lectura 22 del código fuente de Spring: use FactoryBean para crear objetos de instancia de Bean

Este es el día 33 de mi participación en el "Nuggets Daily New Plan·Desafío de actualización de agosto", haga clic para ver los detalles del evento

Basado en Spring Framework v5.2.6.RELEASE

Continuación del artículo anterior: Código fuente de Spring Lectura 21: Dependencias circulares y caché L3

Resumen

El artículo anterior introdujo que en el doGetBeanmétodo de AbstractBeanFactory, después de completar la conversión del nombre del bean, el primer paso es obtener el objeto de instancia del bean singleton del caché de tercer nivel del contenedor Spring.

Si el objeto de instancia de Bean se obtiene en la memoria caché del contenedor, Spring seguirá procesando este objeto. El código de esta parte es el siguiente:

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

Además de la parte de procesamiento de registros, el método se llama principalmente para getObjectForBeanInstanceobtener la instancia de Bean que finalmente se devuelve como resultado.

Después de obtener el objeto de la instancia Bean del caché, ¿qué trabajo de seguimiento debe hacer Spring?Este artículo toma este método como punto de partida para un análisis en profundidad.

Manejo de diferentes tipos de frijoles

En primer lugar, debe prestar atención a los siguientes getObjectForBeanInstanceparámetros al llamar al método, incluidos los siguientes:

  • El objeto de instancia de Bean obtenido del caché sharedInstance, de acuerdo con el juicio anterior, este objeto no está vacío en este momento.
  • doGetBeanEl parámetro del método de llamada name, es decir, el valor antes de la conversión del nombre del bean. El valor de this es también el valor del parámetro pasado cuando se namellama inicialmente al método Se puede decir que representa la intención al llamar al método.getBeangetBean
  • El canónico convertido beanName, es decir, el identificador único del Bean en el contenedor.
  • El último parámetro se pasa en null, y finalmente llega el análisis posterior y luego lo mira en detalle.

A continuación, introduzca el código del método:

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

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

总结

En este punto finaliza el proceso de obtener la instancia del Bean de la caché y getObjectForBeanInstanceobtener el objeto de resultado final a través del método getObjectForBeanInstanceLa función principal del método es tratar con el Bean del tipo que implementa la interfaz FactoryBean. Aquí hay un punto de conocimiento poco conocido, es decir, si el tipo de un bean es la clase de implementación de FactoryBean, entonces si desea obtener la instancia del propio FactoryBean que creó la instancia del bean, puede getBean("&beanName")obtenerlo llamando .

Después de esta parte del proceso, el objeto obtenido aquí es el objeto doGetBeanque devolverá el método al final. Sin embargo, hasta ahora solo hemos analizado doGetBeanuna pequeña parte del método, es decir, el objeto de instancia del Bean se puede obtener en el caché. Si la doGetBeaninstancia del Bean no se obtiene en el caché en el primer paso del método, entonces la instancia del Bean debe inicializarse desde cero. A partir del próximo artículo, analizaremos cómo se inicializa y se obtiene el Bean en este caso.

Supongo que te gusta

Origin juejin.im/post/7136590694340100127
Recomendado
Clasificación