Springのイベントパブリケーションとイベントサブスクリプション(ApplicationEventPublisher)
44963 ワード
Springのイベントパブリッシュツール
通常の開発ではSpringは、イベントのパブリケーションとサブスクリプションを提供する一連のツールを提供しています.事件の発表は主に
イベントのパブリケーションを実現するには、タスク、イベント、イベントのリスニングなど、主に次のオブジェクトが必要です.
タスクタスクは、パブリッシュするイベントの内容を保存するための一般的なクラスです.これは特に制限はなく、自分の業務に応じて自由に設定できます.
イベント・イベント・クラスは、Springによって識別されるように、
イベントリスニング
イベントのリスナーは、
テスト
これにより、次のテスト内容でイベントパブリッシュのテストを完了できます.
Springイベント発行プロセス
Springのイベントパブリケーションとサブスクリプションはかなり簡単ですが、ここではイベントパブリケーション全体の流れを簡単に説明します.
ApplicationEventPublisher
その
AbstractApplicationContext
イベントパブリケーションロジック全体がこのクラスおよびそのサブクラスにあり、その最終的なイベントをパブリッシュする方法は
イベント全体が発行されるコアロジックは g e t p r i c a tionEventMulticaster()は、容器中の ApplicationEventMulticaster.MulticastEvent(アプリケーションEvent,eventType)がイベントのパブリッシュを本格的に行うコンテンツ.
ApplicationEventMulticaster
これは
次のような流れがあります.イベントタイプを取得し、主にSpring Eventの実際のタイプを取得するために使用されます. .は、
AbstractApplicationEventMulticaster
取得リスナーの主なロジック
大体の流れ:時間タイプとイベント内のデータソースタイプを通じて、キャッシュキーを構築し、このキーに対応するイベントプロセッサの有無をキャッシュに取得します.存在しない場合、新しい
retrieveApplicationListeners
この方法で主に行われる論理は簡単です.リスニングマッチングは、主にListenersをループすることによって行われる.Listenersのソースは主に2つの部分です.
一部はコンテナにすでに存在するリスナーであり、一部はリスナーbeanの文字列識別の名前である.アプリケーションListenerBeansは、主に、それらの宣言を処理した後もリスナーセットに追加されていないbeanを処理するために使用されます.
イベントの発行プロセス
イベントパブリッシュツールの取得
イベントのパブリッシュ
イベントリスニングパブリケーションツールの取得
パッチワークイベントリスニングキャッシュkey
Listenerをキャッシュから取り出す
存在する
存在しません.コンテナからListenerのマッチングを開始します.
コンテナのListenerと一致
コンテナ内の文字列に一致するBean
Listenerメソッドの実行
Listenerメソッドの実行
Listenerメソッドの実行
イベントの発行
ApplicationEventPublisher.publishEvent
AbstractApplicationContext.getApplicationEventMulticaster
SimpleApplicationEventMulticaster.multicastEvent
AbstractApplicationEventMulticaster.getApplicationListeners
ListenerCacheKey
retrieverCache
結果を返す
retrieveApplicationListeners
applicationListeners
applicationListenerBeans
Listenerの結果を返す
invokeListener
通常の開発ではSpringは、イベントのパブリケーションとサブスクリプションを提供する一連のツールを提供しています.事件の発表は主に
ApplicationEventPublisher
によって行われた.イベントのパブリケーションを実現するには、タスク、イベント、イベントのリスニングなど、主に次のオブジェクトが必要です.
タスクタスクは、パブリッシュするイベントの内容を保存するための一般的なクラスです.これは特に制限はなく、自分の業務に応じて自由に設定できます.
import lombok.Data;
/**
*
* @author daify
* @date 2019-08-19
**/
@Data
public class Task {
private Long id;
private String taskName;
private String taskContext;
private boolean finish;
}
イベント・イベント・クラスは、Springによって識別されるように、
org.springframework.context.ApplicationEvent
を継承する必要があります.import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEvent;
/**
*
* @author daify
* @date 2019-08-19
**/
@Slf4j
public class MyEvent extends ApplicationEvent {
private Task task;
public MyEvent(Task task) {
super(task);
this.task = task;
}
public Task getTask() {
return task;
}
}
イベントリスニング
イベントのリスナーは、
org.springframework.context.ApplicationListener
を実装する必要があり、容器に注入する必要がある.import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
import java.util.Objects;
/**
*
* @author daify
* @date 2019-08-19
**/
@Component
@Slf4j
public class MyEventListener implements ApplicationListener<MyEvent> {
@Override
public void onApplicationEvent(MyEvent myEvent) {
if (Objects.isNull(myEvent)) {
return;
}
Task task = myEvent.getTask();
log.info(" :{}",JSON.toJSONString(task));
task.setFinish(true);
log.info(" ");
}
}
テスト
これにより、次のテスト内容でイベントパブリッシュのテストを完了できます.
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
*
* @author daify
* @date 2019-08-19
**/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = .class)
@Slf4j
public class EventTest {
@Autowired
private ApplicationEventPublisher eventPublisher;
@Test
public void publishTest() {
Task task = new Task();
task.setId(1L);
task.setTaskName(" ");
task.setTaskContext(" ");
task.setFinish(false);
MyEvent event = new MyEvent(task);
log.info(" ");
eventPublisher.publishEvent(event);
log.info(" ");
}
}
Springイベント発行プロセス
Springのイベントパブリケーションとサブスクリプションはかなり簡単ですが、ここではイベントパブリケーション全体の流れを簡単に説明します.
ApplicationEventPublisher
その
publishEvent
の方法でタスクを発行すると、コードはorgに入る.springframework.context.support.AbstraactApplicationContext論理内.AbstractApplicationContext
イベントパブリケーションロジック全体がこのクラスおよびそのサブクラスにあり、その最終的なイベントをパブリッシュする方法は
publishEvent(Object event, @Nullable ResolvableType eventType)
である. protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
//
if (logger.isTraceEnabled()) {
logger.trace("Publishing event in " + getDisplayName() + ": " + event);
}
//
ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
}
else {
applicationEvent = new PayloadApplicationEvent<>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent) applicationEvent).getResolvableType();
}
}
// , ,
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
}
else {
// ②
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
// ,
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
}
else {
this.parent.publishEvent(event);
}
}
}
イベント全体が発行されるコアロジックは
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
行にあります.コンテンツは2つの部分に分けられます.ApplicationEventMulticaster
を得る.このコンテンツの主なSpringは、タスクのパブリッシュを支援するツールクラスです.ApplicationEventMulticaster
これは
org.springframework.context.event.AbstractApplicationEventMulticaster
実装クラスであり、主にイベントのパブリケーションを支援するために行われ、その内部パブリケーションタスクの主要なコアロジックはmulticastEvent
にある. @Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
次のような流れがあります.
resolveDefaultEventType(event))
getApplicationListeners(event, type)
このイベントおよびイベントタイプをイベントおよびイベントタイプに従って取得するリスナーinvokeListener(listener, event)
を使用してイベントを実行する.AbstractApplicationEventMulticaster
取得リスナーの主なロジック
org.springframework.context.event.AbstractApplicationEventMulticaster
のgetApplicationListeners(event, type)
protected Collection<ApplicationListener<?>> getApplicationListeners(
ApplicationEvent event, ResolvableType eventType) {
Object source = event.getSource();
Class<?> sourceType = (source != null ? source.getClass() : null);
ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
// Quick check for existing entry on ConcurrentHashMap...
ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
if (retriever != null) {
return retriever.getApplicationListeners();
}
if (this.beanClassLoader == null ||
(ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
(sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
// Fully synchronized building and caching of a ListenerRetriever
synchronized (this.retrievalMutex) {
retriever = this.retrieverCache.get(cacheKey);
if (retriever != null) {
return retriever.getApplicationListeners();
}
retriever = new ListenerRetriever(true);
Collection<ApplicationListener<?>> listeners =
retrieveApplicationListeners(eventType, sourceType, retriever);
this.retrieverCache.put(cacheKey, retriever);
return listeners;
}
}
else {
// No ListenerRetriever caching -> no synchronization necessary
return retrieveApplicationListeners(eventType, sourceType, null);
}
}
大体の流れ:時間タイプとイベント内のデータソースタイプを通じて、キャッシュキーを構築し、このキーに対応するイベントプロセッサの有無をキャッシュに取得します.存在しない場合、新しい
ListenerRetriever
が構築され、retrieveApplicationListeners
メソッドがリスニングされたlistenerを取得するために呼び出される.retrieveApplicationListeners
private Collection<ApplicationListener<?>> retrieveApplicationListeners(
ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable ListenerRetriever retriever) {
List<ApplicationListener<?>> allListeners = new ArrayList<>();
Set<ApplicationListener<?>> listeners;
Set<String> listenerBeans;
synchronized (this.retrievalMutex) {
listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);
}
for (ApplicationListener<?> listener : listeners) {
if (supportsEvent(listener, eventType, sourceType)) {
if (retriever != null) {
retriever.applicationListeners.add(listener);
}
allListeners.add(listener);
}
}
if (!listenerBeans.isEmpty()) {
BeanFactory beanFactory = getBeanFactory();
for (String listenerBeanName : listenerBeans) {
try {
Class<?> listenerType = beanFactory.getType(listenerBeanName);
if (listenerType == null || supportsEvent(listenerType, eventType)) {
ApplicationListener<?> listener =
beanFactory.getBean(listenerBeanName, ApplicationListener.class);
if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
if (retriever != null) {
if (beanFactory.isSingleton(listenerBeanName)) {
retriever.applicationListeners.add(listener);
}
else {
retriever.applicationListenerBeans.add(listenerBeanName);
}
}
allListeners.add(listener);
}
}
}
catch (NoSuchBeanDefinitionException ex) {
// Singleton listener instance (without backing bean definition) disappeared -
// probably in the middle of the destruction phase
}
}
}
AnnotationAwareOrderComparator.sort(allListeners);
if (retriever != null && retriever.applicationListenerBeans.isEmpty()) {
retriever.applicationListeners.clear();
retriever.applicationListeners.addAll(allListeners);
}
return allListeners;
}
この方法で主に行われる論理は簡単です.リスニングマッチングは、主にListenersをループすることによって行われる.Listenersのソースは主に2つの部分です.
listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);
一部はコンテナにすでに存在するリスナーであり、一部はリスナーbeanの文字列識別の名前である.アプリケーションListenerBeansは、主に、それらの宣言を処理した後もリスナーセットに追加されていないbeanを処理するために使用されます.
イベントの発行プロセス
イベントパブリッシュツールの取得
イベントのパブリッシュ
イベントリスニングパブリケーションツールの取得
パッチワークイベントリスニングキャッシュkey
Listenerをキャッシュから取り出す
存在する
存在しません.コンテナからListenerのマッチングを開始します.
コンテナのListenerと一致
コンテナ内の文字列に一致するBean
Listenerメソッドの実行
Listenerメソッドの実行
Listenerメソッドの実行
イベントの発行
ApplicationEventPublisher.publishEvent
AbstractApplicationContext.getApplicationEventMulticaster
SimpleApplicationEventMulticaster.multicastEvent
AbstractApplicationEventMulticaster.getApplicationListeners
ListenerCacheKey
retrieverCache
結果を返す
retrieveApplicationListeners
applicationListeners
applicationListenerBeans
Listenerの結果を返す
invokeListener