Avoid transaction listener execution without transaction management setup

Closes gh-32319
This commit is contained in:
Juergen Hoeller
2024-02-23 11:53:05 +01:00
parent bed4d684e6
commit 524588ef93
5 changed files with 79 additions and 15 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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.
@@ -49,8 +49,6 @@ public class TransactionalApplicationListenerMethodAdapter extends ApplicationLi
private final TransactionPhase transactionPhase;
private final boolean fallbackExecution;
private final List<SynchronizationCallback> callbacks = new CopyOnWriteArrayList<>();
@@ -68,7 +66,6 @@ public class TransactionalApplicationListenerMethodAdapter extends ApplicationLi
throw new IllegalStateException("No TransactionalEventListener annotation found on method: " + method);
}
this.transactionPhase = eventAnn.phase();
this.fallbackExecution = eventAnn.fallbackExecution();
}
@@ -91,7 +88,7 @@ public class TransactionalApplicationListenerMethodAdapter extends ApplicationLi
logger.debug("Registered transaction synchronization for " + event);
}
}
else if (this.fallbackExecution) {
else if (isDefaultExecution()) {
if (getTransactionPhase() == TransactionPhase.AFTER_ROLLBACK && logger.isWarnEnabled()) {
logger.warn("Processing " + event + " as a fallback execution on AFTER_ROLLBACK phase");
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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.
@@ -77,11 +77,6 @@ public @interface TransactionalEventListener {
*/
TransactionPhase phase() default TransactionPhase.AFTER_COMMIT;
/**
* Whether the event should be handled if no transaction is running.
*/
boolean fallbackExecution() default false;
/**
* Alias for {@link #classes}.
*/
@@ -107,6 +102,13 @@ public @interface TransactionalEventListener {
@AliasFor(annotation = EventListener.class, attribute = "condition")
String condition() default "";
/**
* Whether the event should be handled if no transaction is running.
* @see EventListener#defaultExecution()
*/
@AliasFor(annotation = EventListener.class, attribute = "defaultExecution")
boolean fallbackExecution() default false;
/**
* An optional identifier for the listener, defaulting to the fully-qualified
* signature of the declaring method (e.g. "mypackage.MyClass.myMethod()").

View File

@@ -269,8 +269,7 @@ class TransactionalEventListenerTests {
@Test
void noTransaction() {
load(BeforeCommitTestListener.class, AfterCompletionTestListener.class,
AfterCompletionExplicitTestListener.class);
load(BeforeCommitTestListener.class, AfterCompletionTestListener.class, AfterCompletionExplicitTestListener.class);
this.context.publishEvent("test");
getEventCollector().assertTotalEventsCount(0);
}
@@ -318,6 +317,24 @@ class TransactionalEventListenerTests {
getEventCollector().assertTotalEventsCount(4);
}
@Test
void noTransactionManagementWithFallbackExecution() {
doLoad(PlainConfiguration.class, FallbackExecutionTestListener.class);
this.context.publishEvent("test");
this.eventCollector.assertEvents(EventCollector.BEFORE_COMMIT, "test");
this.eventCollector.assertEvents(EventCollector.AFTER_COMMIT, "test");
this.eventCollector.assertEvents(EventCollector.AFTER_ROLLBACK, "test");
this.eventCollector.assertEvents(EventCollector.AFTER_COMPLETION, "test");
getEventCollector().assertTotalEventsCount(4);
}
@Test
void noTransactionManagementWithoutFallbackExecution() {
doLoad(PlainConfiguration.class, BeforeCommitTestListener.class, AfterCommitMetaAnnotationTestListener.class);
this.context.publishEvent("test");
this.eventCollector.assertNoEventReceived();
}
@Test
void conditionFoundOnTransactionalEventListener() {
load(ImmediateTestListener.class);
@@ -401,6 +418,31 @@ class TransactionalEventListenerTests {
}
@Configuration
static class PlainConfiguration {
@Bean
public EventCollector eventCollector() {
return new EventCollector();
}
@Bean
public TestBean testBean(ApplicationEventPublisher eventPublisher) {
return new TestBean(eventPublisher);
}
@Bean
public CallCountingTransactionManager transactionManager() {
return new CallCountingTransactionManager();
}
@Bean
public TransactionTemplate transactionTemplate() {
return new TransactionTemplate(transactionManager());
}
}
@Configuration
static class MulticasterWithCustomExecutor {