Spring ソースコードの読み方 17: 遅延ロードされていないシングルトン Bean を初期化する

「ナゲッツデイリー新プラン・8月アップデートチャレンジ」参加31日目、イベント詳細はこちら

Spring Framework v5.2.6.RELEASE に基づく

前回記事の続き:Springのソースコード読解16:イベントブロードキャスターの初期化&イベントリスナーの登録

要約

前回の記事でApplicationContext が Spring コンテナを初期化すると述べましたが、このAbstractApplicationContext#refreshメソッドは非常に重要なメソッドであり、Spring コンテナの初期化の全プロセスが含まれます。最近の一連の記事では、このメソッドの各ステップの特定の原則を詳細に分析しています. この記事では、非遅延ロード シングルトン Bean を初期化するプロセス、つまりrefreshメソッドの次のコード行を分析しています。

finishBeanFactoryInitialization(beanFactory);
复制代码

非遅延ロード Bean の初期化

最初に理解しておく必要がある知識のポイントは次のとおりです。

Spring では、すべての通常のシングルトン Bean はデフォルトで遅延読み込みをサポートしていません。つまり、コンテナーの初期化時に作成されるため、構成エラーをできるだけ早く検出できるという利点があります。

XML構成のbeanタグにはlazy-init属性があり、それに対応lazyInitしてBeanDefinitionにもメンバ変数があり、両者は対応しています。デフォルト値は ですfalseIf required, we can also set this value to true, so that the singleton bean will only be created and initialized when it is received from the container for first time. それ以前は、コンテナは実際にはこの Bean の実際のインスタンスが存在しません。

これらを理解した後、finishBeanFactoryInitializationメソッドを入力して詳細なコードを表示します。

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
   // Initialize conversion service for this context.
   if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
         beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
      beanFactory.setConversionService(
            beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
   }

   // Register a default embedded value resolver if no bean post-processor
   // (such as a PropertyPlaceholderConfigurer bean) registered any before:
   // at this point, primarily for resolution in annotation attribute values.
   if (!beanFactory.hasEmbeddedValueResolver()) {
      beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
   }

   // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
   String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
   for (String weaverAwareName : weaverAwareNames) {
      getBean(weaverAwareName);
   }

   // Stop using the temporary ClassLoader for type matching.
   beanFactory.setTempClassLoader(null);

   // Allow for caching all bean definition metadata, not expecting further changes.
   beanFactory.freezeConfiguration();

   // Instantiate all remaining (non-lazy-init) singletons.
   beanFactory.preInstantiateSingletons();
}
复制代码

いくつかの事前作業

代码中的第一个if语句块,是为了在beanFactory中查找一个特定的 ConversionService 类型的 Bean,如果没有的话,就创建一个并注册到容器中。这是一个 Spring 用来处理类型转换的 Bean。

第二个if语句块,用同样的方法注册了一个 StringValueResolver 类型的 Bean,它是一个用来处理注解属性的字符串内容解析器。我们还没有涉及到注解相关的部分,这里就先略过。

接下来是这部分代码:

// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
for (String weaverAwareName : weaverAwareNames) {
  getBean(weaverAwareName);
}
// Stop using the temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(null);
复制代码

这里和之前在 BeanFactory 预处理阶段的prepareBeanFactory方法中的以下代码片段对应:

// Detect a LoadTimeWeaver and prepare for weaving, if found.
if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
   beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
   // Set a temporary ClassLoader for type matching.
   beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
复制代码

主要是一些与 AspectJ 相关的流程,提前初始化了 LoadTimeWeaverAware 类型的 Bean,并把之前设置的 TempClassLoader 移除了。更多细节,我们这里也不做介绍。

方法的倒数第二行代码,调用了容器的freezeConfiguration方法,也就是冻结配置。这个方法的作用就是将已经注册的 BeanDefinition 配置进行冻结,也就是,执行这个方法之后,这些 BeanDefinition 就不能再被做任何修改了。

初始化非懒加载的 Bean

方法的最后一行代码,才是关键的步骤,就是调用容器的preInstantiateSingletons方法来初始化非懒加载的单例 Bean。方法的源码如下:

@Override
public void preInstantiateSingletons() throws BeansException {
   if (logger.isTraceEnabled()) {
      logger.trace("Pre-instantiating singletons in " + this);
   }

   // Iterate over a copy to allow for init methods which in turn register new bean definitions.
   // While this may not be part of the regular factory bootstrap, it does otherwise work fine.
   List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

   // Trigger initialization of all non-lazy singleton beans...
   for (String beanName : beanNames) {
      RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
      if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
         if (isFactoryBean(beanName)) {
            Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
            if (bean instanceof FactoryBean) {
               final FactoryBean<?> factory = (FactoryBean<?>) bean;
               boolean isEagerInit;
               if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
                  isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
                              ((SmartFactoryBean<?>) factory)::isEagerInit,
                        getAccessControlContext());
               }
               else {
                  isEagerInit = (factory instanceof SmartFactoryBean &&
                        ((SmartFactoryBean<?>) factory).isEagerInit());
               }
               if (isEagerInit) {
                  getBean(beanName);
               }
            }
         }
         else {
            getBean(beanName);
         }
      }
   }

   // Trigger post-initialization callback for all applicable beans...
   for (String beanName : beanNames) {
      Object singletonInstance = getSingleton(beanName);
      if (singletonInstance instanceof SmartInitializingSingleton) {
         final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
         if (System.getSecurityManager() != null) {
            AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
               smartSingleton.afterSingletonsInstantiated();
               return null;
            }, getAccessControlContext());
         }
         else {
            smartSingleton.afterSingletonsInstantiated();
         }
      }
   }
}
复制代码

这个方法的代码量也不少,但是主要逻辑很简单。

从容器中拿到所有的beanName,如果它不是抽象Bean、且是单例的、且被配置为不允许懒加载,那么就会通过getBean方法获取 Bean 的实例,此时,如果是第一次获取某个 Bean,它就会被初始化。在此过程中,如果判断出一个 Bean 是工厂 Bean,那么还会做响应的处理。

実際、このプロセスでは、これらの Bean はすべてシングルトンであるため、Spring はsingletonObjectsBean がインスタンス化された後にそれらをメンバー変数に入れgetSingleton(String beanName)、メソッドを通じて Bean のインスタンスを取得できます。

これらの Bean が初期化されると、それらのメソッドは SmartInitializingSingleton インターフェイスを実装するインスタンスで呼び出されますafterSingletonsInstantiatedこのインターフェースは、この 1 つのメソッドのみを定義します。その目的は、Bean が初期化後にいくつかの操作を実行できるようにする拡張ポイントを提供することです。

ファローアップ

これまでのところ、遅延ロードされていない Bean を初期化するプロセスも紹介されています。refresh次の分析では、最後のステップが 1 つだけ残っています

おすすめ

転載: juejin.im/post/7135993461504737317