Support for always executing specific listeners in original thread

See gh-30244
This commit is contained in:
Juergen Hoeller
2023-08-01 23:26:35 +02:00
parent dde8f4489f
commit a9d100eeee
4 changed files with 57 additions and 6 deletions

View File

@@ -48,6 +48,18 @@ public interface ApplicationListener<E extends ApplicationEvent> extends EventLi
*/
void onApplicationEvent(E event);
/**
* Return whether this listener supports asynchronous execution.
* @return {@code true} if this listener instance can be executed asynchronously
* depending on the multicaster configuration (the default), or {@code false} if it
* needs to immediately run within the original thread which published the event
* @since 6.1
* @see org.springframework.context.event.SimpleApplicationEventMulticaster#setTaskExecutor
*/
default boolean supportsAsyncExecution() {
return true;
}
/**
* Create a new {@code ApplicationListener} for the given payload consumer.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -110,7 +110,10 @@ public interface ApplicationEventMulticaster {
* Multicast the given application event to appropriate listeners.
* <p>Consider using {@link #multicastEvent(ApplicationEvent, ResolvableType)}
* if possible as it provides better support for generics-based events.
* <p>If a matching {@code ApplicationListener} does not support asynchronous
* execution, it must be run within the calling thread of this multicast call.
* @param event the event to multicast
* @see ApplicationListener#supportsAsyncExecution()
*/
void multicastEvent(ApplicationEvent event);
@@ -118,9 +121,12 @@ public interface ApplicationEventMulticaster {
* Multicast the given application event to appropriate listeners.
* <p>If the {@code eventType} is {@code null}, a default type is built
* based on the {@code event} instance.
* <p>If a matching {@code ApplicationListener} does not support asynchronous
* execution, it must be run within the calling thread of this multicast call.
* @param event the event to multicast
* @param eventType the type of event (can be {@code null})
* @since 4.2
* @see ApplicationListener#supportsAsyncExecution()
*/
void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType);

View File

@@ -79,10 +79,15 @@ public class SimpleApplicationEventMulticaster extends AbstractApplicationEventM
* to invoke each listener with.
* <p>Default is equivalent to {@link org.springframework.core.task.SyncTaskExecutor},
* executing all listeners synchronously in the calling thread.
* <p>Consider specifying an asynchronous task executor here to not block the
* caller until all listeners have been executed. However, note that asynchronous
* execution will not participate in the caller's thread context (class loader,
* transaction association) unless the TaskExecutor explicitly supports this.
* <p>Consider specifying an asynchronous task executor here to not block the caller
* until all listeners have been executed. However, note that asynchronous execution
* will not participate in the caller's thread context (class loader, transaction context)
* unless the TaskExecutor explicitly supports this.
* <p>{@link ApplicationListener} instances which declare no support for asynchronous
* execution ({@link ApplicationListener#supportsAsyncExecution()} always run within
* the original thread which published the event, e.g. the transaction-synchronized
* {@link org.springframework.transaction.event.TransactionalApplicationListener}.
* @since 2.0
* @see org.springframework.core.task.SyncTaskExecutor
* @see org.springframework.core.task.SimpleAsyncTaskExecutor
*/
@@ -92,6 +97,7 @@ public class SimpleApplicationEventMulticaster extends AbstractApplicationEventM
/**
* Return the current task executor for this multicaster.
* @since 2.0
*/
@Nullable
protected Executor getTaskExecutor() {
@@ -136,7 +142,7 @@ public class SimpleApplicationEventMulticaster extends AbstractApplicationEventM
ResolvableType type = (eventType != null ? eventType : ResolvableType.forInstance(event));
Executor executor = getTaskExecutor();
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) {
if (executor != null && listener.supportsAsyncExecution()) {
executor.execute(() -> invokeListener(listener, event));
}
else {