Lecture du code source Spring 22 : Utilisez FactoryBean pour créer des objets d'instance Bean

C'est le 33ème jour de ma participation au "Nuggets Daily New Plan · August Update Challenge", cliquez pour voir les détails de l'événement

Basé sur Spring Framework v5.2.6.RELEASE

Suite de l'article précédent : Spring Source Code Reading 21 : Dépendances circulaires et cache L3

résumer

L'article précédent introduisait que dans la doGetBeanméthode AbstractBeanFactory, après avoir terminé la conversion du nom du bean, la première étape consiste à obtenir l'objet instance du bean singleton à partir du cache de troisième niveau du conteneur Spring.

Si l'objet d'instance Bean est obtenu dans le cache du conteneur, Spring traitera davantage cet objet. Le code de cette partie est le suivant :

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

En plus de la partie traitement du journal, la méthode est principalement appelée pour getObjectForBeanInstanceobtenir l'instance Bean qui est finalement renvoyée comme résultat.

Après avoir obtenu l'objet d'instance Bean à partir du cache, quel travail de suivi doit faire Spring Cet article prend cette méthode comme point de départ pour une analyse approfondie.

Manipulation de différents types de haricots

Tout d'abord, vous devez faire attention aux getObjectForBeanInstanceparamètres suivants lors de l'appel de la méthode, notamment les suivants :

  • L'objet d'instance Bean obtenu à partir du cache sharedInstance, selon le jugement si précédent, cet objet n'est pas vide à ce moment.
  • doGetBeanLe paramètre de la méthode appelante name, c'est-à-dire la valeur avant la conversion du nom du bean. La valeur de this est également la valeur du paramètre passé lors de l' nameappel initial de la méthode On peut dire qu'il représente l' intention lors de l'appel de la méthode.getBeangetBean
  • Le canonical converti beanName, c'est-à-dire l'identifiant unique du Bean dans le conteneur.
  • Le dernier paramètre est passé dans null, et l'analyse suivante arrive enfin et l'examine en détail.

Saisissez ensuite le code de la méthode :

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

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

总结

À ce stade, le processus d'obtention de l'instance du bean à partir du cache et d' getObjectForBeanInstanceobtention de l'objet de résultat final via la méthode est terminé. getObjectForBeanInstanceLa fonction principale de la méthode est de traiter le bean du type qui implémente l'interface FactoryBean. Il y a un point de connaissance peu connu ici, c'est-à-dire que si le type d'un bean est la classe d'implémentation de FactoryBean, alors si vous voulez obtenir l'instance du FactoryBean lui-même qui a créé l'instance du bean, vous pouvez getBean("&beanName")l'obtenir en appelant .

Après cette partie du processus, l'objet obtenu ici est l'objet doGetBeanà renvoyer par la méthode à la fin. Cependant, jusqu'à présent, nous n'avons analysé doGetBeanqu'une petite partie de la méthode, c'est-à-dire que l'objet instance du Bean peut être obtenu dans le cache. Si l' doGetBeaninstance du Bean n'est pas obtenue dans le cache lors de la première étape de la méthode, alors l'instance du Bean doit être initialisée à partir de zéro. À partir du prochain article, nous analyserons comment le Bean est initialisé et obtenu dans ce cas. .

Je suppose que tu aimes

Origine juejin.im/post/7136590694340100127
conseillé
Classement