Spring ソースコードの読み取り 18: Spring コンテキスト初期化の最終ステップ

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

Spring Framework v5.2.6.RELEASE に基づく

前回の記事からの続き: Spring ソースコードの読み取り 17: 非遅延ロードのシングルトン Bean を初期化する

要約

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

// Last step: publish corresponding event.
finishRefresh();
复制代码

初期化の最終段階

メソッドのコードは次のとおりです。

protected void finishRefresh() {
   // Clear context-level resource caches (such as ASM metadata from scanning).
   clearResourceCaches();
   // Initialize lifecycle processor for this context.
   initLifecycleProcessor();
   // Propagate refresh to lifecycle processor first.
   getLifecycleProcessor().onRefresh();
   // Publish the final event.
   publishEvent(new ContextRefreshedEvent(this));
   // Participate in LiveBeansView MBean, if active.
   LiveBeansView.registerApplicationContext(this);
}
复制代码

数行のメソッド呼び出し、1 つずつ分析してみましょう。

1 行目、clearResourceCaches メソッドを呼び出す機能は、主にリソース読み込みのキャッシュをクリアすることです。それから下を向いてください。

春のライフサイクル

次の 2 行のコードは、Spring のライフサイクルに関連しています。

// Initialize lifecycle processor for this context.
initLifecycleProcessor();
// Propagate refresh to lifecycle processor first.
getLifecycleProcessor().onRefresh();
复制代码

最初に LifecycleProcessor を初期化します。コードは次のとおりです。

protected void initLifecycleProcessor() {
   ConfigurableListableBeanFactory beanFactory = getBeanFactory();
   if (beanFactory.containsLocalBean(LIFECYCLE_PROCESSOR_BEAN_NAME)) {
      this.lifecycleProcessor =
            beanFactory.getBean(LIFECYCLE_PROCESSOR_BEAN_NAME, LifecycleProcessor.class);
      if (logger.isTraceEnabled()) {
         logger.trace("Using LifecycleProcessor [" + this.lifecycleProcessor + "]");
      }
   }
   else {
      DefaultLifecycleProcessor defaultProcessor = new DefaultLifecycleProcessor();
      defaultProcessor.setBeanFactory(beanFactory);
      this.lifecycleProcessor = defaultProcessor;
      beanFactory.registerSingleton(LIFECYCLE_PROCESSOR_BEAN_NAME, this.lifecycleProcessor);
      if (logger.isTraceEnabled()) {
         logger.trace("No '" + LIFECYCLE_PROCESSOR_BEAN_NAME + "' bean, using " +
               "[" + this.lifecycleProcessor.getClass().getSimpleName() + "]");
      }
   }
}
复制代码

まず、コンテナーに最上位の名前の Bean が含まれているかどうかを判断し、含まれている場合は、それを取得しlifecycleProcessorて現在のコンテキストのメンバー変数に割り当てます。

そうでない場合は、 DefaultLifecycleProcessor タイプを作成してdefaultProcessorに割り当てlifecycleProcessor、コンテナに登録します。

初期化はここで完了します。これは、Spring の多くの特別な Bean の初期化プロセスに似ています。

次に、getLifecycleProcessor().onRefresh()このコード行を見てください。

getLifecycleProcessorメソッドは、実際にはメンバー変数から初期化したばかりの LifecycleProcessor を取得するためのものなので、そのメソッドを直接lifecycleProcessor見てみましょう。onRefresh

@Override
public void onRefresh() {
   startBeans(true);
   this.running = true;
}

private void startBeans(boolean autoStartupOnly) {
   Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans();
   Map<Integer, LifecycleGroup> phases = new HashMap<>();
   lifecycleBeans.forEach((beanName, bean) -> {
      if (!autoStartupOnly || (bean instanceof SmartLifecycle && ((SmartLifecycle) bean).isAutoStartup())) {
         int phase = getPhase(bean);
         LifecycleGroup group = phases.get(phase);
         if (group == null) {
            group = new LifecycleGroup(phase, this.timeoutPerShutdownPhase, lifecycleBeans, autoStartupOnly);
            phases.put(phase, group);
         }
         group.add(beanName, bean);
      }
   });
   if (!phases.isEmpty()) {
      List<Integer> keys = new ArrayList<>(phases.keySet());
      Collections.sort(keys);
      for (Integer key : keys) {
         phases.get(key).start();
      }
   }
}
复制代码

这里的逻辑简单概括就是,从容器中获取所有实现了 Lifecycle 接口的 Bean,然后调用他们的start方法。

最后还有必要介绍一下刚才提到的几个接口和类型。以下是他们的关系图。

Lifecycle 顾名思义就是生命周期的意思,接口中定义了一系列方法,实现了这个接口的 Bean,会在 Spring 生命周期的特定阶段被调用相依的方法。

比如,在刚刚的介绍中,所有实现了 Lifecycle 的 Bean 会在 Spring 上下文启动时调用start也就意味着,这些 Bean 的start方法中的逻辑会在 Spring 上下文启东时被执行。LifecycleProcessor 接口在此基础上,添加了两个生命周期方法。其中就包括刚刚分析的代码中被调用的onRefresh方法。

事件发布

接着看后面的步骤:

publishEvent(new ContextRefreshedEvent(this));
复制代码

这里调用了publishEvent方法,发布了一个 ContextRefreshedEvent 事件。事件发布之后,事件广播器就会将这个事件广播给所有要处理这个事件的监听器,执行其中的onApplicationEvent方法。

关于事件广播器的原理、事件监听器的注册、以及事件广播的过程,在之前的源码分析中已经介绍过了,这里不再详细介绍,可以参考:Spring 源码阅读 16:初始化事件广播器 & 注册事件监听器

这里主要从publishEvent方法的源码中分析一下事件发布的原理。

@Override
public void publishEvent(ApplicationEvent event) {
   publishEvent(event, null);
}

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
   Assert.notNull(event, "Event must not be null");
   // Decorate event as an ApplicationEvent if necessary
   ApplicationEvent applicationEvent;
   if (event instanceof ApplicationEvent) {
      applicationEvent = (ApplicationEvent) event;
   } else {
      applicationEvent = new PayloadApplicationEvent<>(this, event);
      if (eventType == null) {
         eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
      }
   }
   // Multicast right now if possible - or lazily once the multicaster is initialized
   if (this.earlyApplicationEvents != null) {
      this.earlyApplicationEvents.add(applicationEvent);
   } else {
      getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
   }
   // Publish event via parent context as well...
   if (this.parent != null) {
      if (this.parent instanceof AbstractApplicationContext) {
         ((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
      } else {
         this.parent.publishEvent(event);
      }
   }
}
复制代码

这里的流程主要分三步:

  1. 先判断事件是不是 ApplicationEvent 的实例,之前流程中创建的 ContextRefreshedEvent 实现了 ApplicationEvent 接口,因此这里判断条件结果为true
  2. イベント放送局が初期化されているか確認してください。そうでない場合は、現在の時刻をコレクションに追加し、earlyApplicationEventsブロードキャストの前にイベント ブロードキャスターが初期化されるのを待ちます。初期化されている場合、メソッドが呼び出さmulticastEventれてイベントがブロードキャストされます。multicastEventメソッドのプロセスは以前に紹介されています。Spring ソース コードの読み取り 16: イベント ブロードキャスターの初期化とイベント リスナーの登録を参照してください。
  3. 現在のコンテキストに親コンテキストが存在する場合、イベントは親コンテキストで発行されます。

もっと

最後のステップ:

LiveBeansView.registerApplicationContext(this);
复制代码

Spring コンテナを LiveBeansView に登録することは、私たちの範囲を超えています。

ファローアップ

この時点で Spring コンテナーの初期化のプロセスが分析され、合計 18 の記事が作成されました。フォローアップでは、Spring の Bean の初期化のプロセスをgetBean、これまで何度も見てきたメソッドから分析しますので、ご期待ください。

おすすめ

転載: juejin.im/post/7135994676632354847