Leitura de código fonte Spring 22: Use FactoryBean para criar objetos de instância Bean

Este é o 33º dia da minha participação no "Nuggets Daily New Plan·August Update Challenge", clique para ver os detalhes do evento

Baseado no Spring Framework v5.2.6.RELEASE

Continuação do artigo anterior: Spring Source Code Reading 21: Circular Dependencies and L3 Cache

Recapitular

O artigo anterior introduziu que no doGetBeanmétodo de AbstractBeanFactory, após concluir a conversão do nome do bean, o primeiro passo é obter o objeto de instância do bean singleton do cache de terceiro nível do contêiner Spring.

Se o objeto de instância do Bean for obtido no cache do contêiner, o Spring processará ainda mais esse objeto. O código desta parte é o seguinte:

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

Além da parte de processamento de log, o método é chamado principalmente para getObjectForBeanInstanceobter a instância do Bean que é finalmente retornada como resultado.

Depois de obter o objeto de instância Bean do cache, que trabalho de acompanhamento o Spring precisa fazer Este artigo usa esse método como ponto de partida para uma análise aprofundada.

Manipulação de diferentes tipos de feijão

Antes de tudo, você precisa prestar atenção aos seguintes getObjectForBeanInstanceparâmetros ao chamar o método, incluindo o seguinte:

  • O objeto instância do Bean obtido do cache sharedInstance, de acordo com o julgamento anterior, este objeto não está vazio neste momento.
  • doGetBeanO parâmetro do método de chamada name, ou seja, o valor antes da conversão do nome do bean. O valor deste também é o valor do parâmetro passado quando o método é namechamado inicialmente , pode-se dizer que representa a intenção ao chamar o método.getBeangetBean
  • O canônico convertido beanName, ou seja, o identificador exclusivo do Bean no contêiner.
  • O último parâmetro é passado em null, e a análise subsequente finalmente chega e a examina em detalhes.

Em seguida digite o código do 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;
}
复制代码

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

总结

Neste ponto, o processo de obtenção da instância do Bean do cache e getObjectForBeanInstanceobtenção do objeto de resultado final através do método é finalizado.A getObjectForBeanInstanceprincipal função do método é lidar com o Bean do tipo que implementa a interface FactoryBean. Há um ponto de conhecimento pouco conhecido aqui, ou seja, se o tipo de um bean for a classe de implementação do FactoryBean, então se você quiser obter a instância do próprio FactoryBean que criou a instância do bean, você pode getBean("&beanName")obtê-lo chamando .

Após esta parte do processo, o objeto obtido aqui é o objeto doGetBeana ser retornado pelo método ao final. No entanto, até agora, analisamos apenas doGetBeanuma pequena parte do método, ou seja, o objeto instância do Bean pode ser obtido no cache. Se a doGetBeaninstância do Bean não for obtida no cache na primeira etapa do método, então a instância do Bean precisa ser inicializada do zero. A partir do próximo artigo, analisaremos como o Bean é inicializado e obtido neste caso. .

Acho que você gosta

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