Closed
Description
If the ApplicationEventMulticaster bean appears in the context, the @TransactionalEventListener annotation is useless, it will not work a priori and is not mentioned anywhere.
At the stage of this method, there is still a transaction in the publish event call chain, SimpleApplicationEventMulticaster.multicastEvent:
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
Executor executor = getTaskExecutor();
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
If we have an ApplicationEventMulticaster bean, then the first if will be executed and invokeListener will be executed deferred in a separate thread WHERE OUR TRANSACTION WILL NOT BE ALREADY.
Here's what's happening in invokeListener -> doInvokeListener -> TransactionalApplicationListenerMethodAdapter.onApplicationEvent():
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (TransactionSynchronizationManager.isSynchronizationActive() &&
TransactionSynchronizationManager.isActualTransactionActive()) {
TransactionSynchronizationManager.registerSynchronization(
new TransactionalApplicationListenerSynchronization<>(event, this, this.callbacks));
}
else if (this.annotation.fallbackExecution()) {
if (this.annotation.phase() == TransactionPhase.AFTER_ROLLBACK && logger.isWarnEnabled()) {
logger.warn("Processing " + event + " as a fallback execution on AFTER_ROLLBACK phase");
}
processEvent(event);
}
else {
// No transactional event execution at all
if (logger.isDebugEnabled()) {
logger.debug("No transaction is active - skipping " + event);
}
}
}
When this code is executed asynchronously in a separate thread (executor), the transaction no longer exists.