From 67e88f3c2023e6b5ec42995b412ccf1a8247c911 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 1 Aug 2025 21:15:25 +0200 Subject: [PATCH 1/2] Align task execution tracking and thread interruption on shutdown Closes gh-35254 --- .../scheduling/concurrent/SimpleAsyncTaskScheduler.java | 2 +- .../org/springframework/core/task/SimpleAsyncTaskExecutor.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/scheduling/concurrent/SimpleAsyncTaskScheduler.java b/spring-context/src/main/java/org/springframework/scheduling/concurrent/SimpleAsyncTaskScheduler.java index 0f3cf3d6b5..9d7d61d52c 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/concurrent/SimpleAsyncTaskScheduler.java +++ b/spring-context/src/main/java/org/springframework/scheduling/concurrent/SimpleAsyncTaskScheduler.java @@ -376,7 +376,7 @@ public class SimpleAsyncTaskScheduler extends SimpleAsyncTaskExecutor implements @Override public boolean isRunning() { - return this.triggerLifecycle.isRunning(); + return (this.triggerLifecycle.isRunning() || this.fixedDelayLifecycle.isRunning()); } @Override diff --git a/spring-core/src/main/java/org/springframework/core/task/SimpleAsyncTaskExecutor.java b/spring-core/src/main/java/org/springframework/core/task/SimpleAsyncTaskExecutor.java index adb9eae4aa..33b35c4b37 100644 --- a/spring-core/src/main/java/org/springframework/core/task/SimpleAsyncTaskExecutor.java +++ b/spring-core/src/main/java/org/springframework/core/task/SimpleAsyncTaskExecutor.java @@ -364,7 +364,6 @@ public class SimpleAsyncTaskExecutor extends CustomizableThreadCreator this.active = false; Set threads = this.activeThreads; if (threads != null) { - threads.forEach(Thread::interrupt); synchronized (threads) { try { if (!threads.isEmpty()) { @@ -375,6 +374,7 @@ public class SimpleAsyncTaskExecutor extends CustomizableThreadCreator Thread.currentThread().interrupt(); } } + threads.forEach(Thread::interrupt); } } } From da13a246040602249db6e509114173f326e160ae Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 1 Aug 2025 21:15:56 +0200 Subject: [PATCH 2/2] Allow any @Transactional propagation for listener with BEFORE_COMMIT phase Closes gh-35150 --- ...ctedTransactionalEventListenerFactory.java | 28 +++++++++++-------- ...ApplicationListenerMethodAdapterTests.java | 12 ++++++++ 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/spring-tx/src/main/java/org/springframework/transaction/annotation/RestrictedTransactionalEventListenerFactory.java b/spring-tx/src/main/java/org/springframework/transaction/annotation/RestrictedTransactionalEventListenerFactory.java index d480170357..bcf3684625 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/annotation/RestrictedTransactionalEventListenerFactory.java +++ b/spring-tx/src/main/java/org/springframework/transaction/annotation/RestrictedTransactionalEventListenerFactory.java @@ -20,6 +20,8 @@ import java.lang.reflect.Method; import org.springframework.context.ApplicationListener; import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalApplicationListenerMethodAdapter; import org.springframework.transaction.event.TransactionalEventListenerFactory; /** @@ -37,20 +39,22 @@ public class RestrictedTransactionalEventListenerFactory extends TransactionalEv @Override public ApplicationListener createApplicationListener(String beanName, Class type, Method method) { - Transactional txAnn = AnnotatedElementUtils.findMergedAnnotation(method, Transactional.class); - - if (txAnn == null) { - txAnn = AnnotatedElementUtils.findMergedAnnotation(type, Transactional.class); - } - - if (txAnn != null) { - Propagation propagation = txAnn.propagation(); - if (propagation != Propagation.REQUIRES_NEW && propagation != Propagation.NOT_SUPPORTED) { - throw new IllegalStateException("@TransactionalEventListener method must not be annotated with " + - "@Transactional unless when declared as REQUIRES_NEW or NOT_SUPPORTED: " + method); + TransactionalApplicationListenerMethodAdapter adapter = + new TransactionalApplicationListenerMethodAdapter(beanName, type, method); + if (adapter.getTransactionPhase() != TransactionPhase.BEFORE_COMMIT) { + Transactional txAnn = AnnotatedElementUtils.findMergedAnnotation(method, Transactional.class); + if (txAnn == null) { + txAnn = AnnotatedElementUtils.findMergedAnnotation(type, Transactional.class); + } + if (txAnn != null) { + Propagation propagation = txAnn.propagation(); + if (propagation != Propagation.REQUIRES_NEW && propagation != Propagation.NOT_SUPPORTED) { + throw new IllegalStateException("@TransactionalEventListener method must not be annotated with " + + "@Transactional unless when declared as REQUIRES_NEW or NOT_SUPPORTED: " + method); + } } } - return super.createApplicationListener(beanName, type, method); + return adapter; } } diff --git a/spring-tx/src/test/java/org/springframework/transaction/event/TransactionalApplicationListenerMethodAdapterTests.java b/spring-tx/src/test/java/org/springframework/transaction/event/TransactionalApplicationListenerMethodAdapterTests.java index 54283661cc..aa32b9f37a 100644 --- a/spring-tx/src/test/java/org/springframework/transaction/event/TransactionalApplicationListenerMethodAdapterTests.java +++ b/spring-tx/src/test/java/org/springframework/transaction/event/TransactionalApplicationListenerMethodAdapterTests.java @@ -157,6 +157,13 @@ class TransactionalApplicationListenerMethodAdapterTests { assertThatNoException().isThrownBy(() -> factory.createApplicationListener("test", SampleEvents.class, m)); } + @Test + void withTransactionalAnnotationBeforeCommit() { + RestrictedTransactionalEventListenerFactory factory = new RestrictedTransactionalEventListenerFactory(); + Method m = ReflectionUtils.findMethod(SampleEvents.class, "withTransactionalAnnotationBeforeCommit", String.class); + assertThatNoException().isThrownBy(() -> factory.createApplicationListener("test", SampleEvents.class, m)); + } + @Test void withTransactionalAnnotationOnEnclosingClass() { RestrictedTransactionalEventListenerFactory factory = new RestrictedTransactionalEventListenerFactory(); @@ -277,6 +284,11 @@ class TransactionalApplicationListenerMethodAdapterTests { public void withAsyncTransactionalAnnotation(String data) { } + @TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT) + @Transactional + public void withTransactionalAnnotationBeforeCommit(String data) { + } + @Transactional static class SampleEventsWithTransactionalAnnotation {