「ナゲッツデイリー新プラン・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);
}
}
}
复制代码
这里的流程主要分三步:
- 先判断事件是不是 ApplicationEvent 的实例,之前流程中创建的 ContextRefreshedEvent 实现了 ApplicationEvent 接口,因此这里判断条件结果为
true
。 - イベント放送局が初期化されているか確認してください。そうでない場合は、現在の時刻をコレクションに追加し、
earlyApplicationEvents
ブロードキャストの前にイベント ブロードキャスターが初期化されるのを待ちます。初期化されている場合、メソッドが呼び出さmulticastEvent
れてイベントがブロードキャストされます。multicastEvent
メソッドのプロセスは以前に紹介されています。Spring ソース コードの読み取り 16: イベント ブロードキャスターの初期化とイベント リスナーの登録を参照してください。 - 現在のコンテキストに親コンテキストが存在する場合、イベントは親コンテキストで発行されます。
もっと
最後のステップ:
LiveBeansView.registerApplicationContext(this);
复制代码
Spring コンテナを LiveBeansView に登録することは、私たちの範囲を超えています。
ファローアップ
この時点で Spring コンテナーの初期化のプロセスが分析され、合計 18 の記事が作成されました。フォローアップでは、Spring の Bean の初期化のプロセスをgetBean
、これまで何度も見てきたメソッドから分析しますので、ご期待ください。