diff --git a/spring-context/src/main/java/org/springframework/context/event/SimpleApplicationEventMulticaster.java b/spring-context/src/main/java/org/springframework/context/event/SimpleApplicationEventMulticaster.java index d1f9b8ca330..2de53ce2902 100644 --- a/spring-context/src/main/java/org/springframework/context/event/SimpleApplicationEventMulticaster.java +++ b/spring-context/src/main/java/org/springframework/context/event/SimpleApplicationEventMulticaster.java @@ -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. @@ -79,10 +79,11 @@ public class SimpleApplicationEventMulticaster extends AbstractApplicationEventM * to invoke each listener with. *

Default is equivalent to {@link org.springframework.core.task.SyncTaskExecutor}, * executing all listeners synchronously in the calling thread. - *

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. + *

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. + * @since 2.0 * @see org.springframework.core.task.SyncTaskExecutor * @see org.springframework.core.task.SimpleAsyncTaskExecutor */ @@ -92,6 +93,7 @@ public class SimpleApplicationEventMulticaster extends AbstractApplicationEventM /** * Return the current task executor for this multicaster. + * @since 2.0 */ @Nullable protected Executor getTaskExecutor() { diff --git a/spring-context/src/test/java/org/springframework/scheduling/annotation/EnableSchedulingTests.java b/spring-context/src/test/java/org/springframework/scheduling/annotation/EnableSchedulingTests.java index 25f59ac8bdd..dc718fdeace 100644 --- a/spring-context/src/test/java/org/springframework/scheduling/annotation/EnableSchedulingTests.java +++ b/spring-context/src/test/java/org/springframework/scheduling/annotation/EnableSchedulingTests.java @@ -63,7 +63,7 @@ public class EnableSchedulingTests { ctx = new AnnotationConfigApplicationContext(FixedRateTaskConfig.class); assertThat(ctx.getBean(ScheduledTaskHolder.class).getScheduledTasks()).hasSize(2); - Thread.sleep(100); + Thread.sleep(110); assertThat(ctx.getBean(AtomicInteger.class).get()).isGreaterThanOrEqualTo(10); } @@ -73,7 +73,7 @@ public class EnableSchedulingTests { ctx = new AnnotationConfigApplicationContext(FixedRateTaskConfigSubclass.class); assertThat(ctx.getBean(ScheduledTaskHolder.class).getScheduledTasks()).hasSize(2); - Thread.sleep(100); + Thread.sleep(110); assertThat(ctx.getBean(AtomicInteger.class).get()).isGreaterThanOrEqualTo(10); } @@ -83,7 +83,7 @@ public class EnableSchedulingTests { ctx = new AnnotationConfigApplicationContext(ExplicitSchedulerConfig.class); assertThat(ctx.getBean(ScheduledTaskHolder.class).getScheduledTasks()).hasSize(1); - Thread.sleep(100); + Thread.sleep(110); assertThat(ctx.getBean(AtomicInteger.class).get()).isGreaterThanOrEqualTo(10); assertThat(ctx.getBean(ExplicitSchedulerConfig.class).threadName).startsWith("explicitScheduler-"); assertThat(Arrays.asList(ctx.getDefaultListableBeanFactory().getDependentBeans("myTaskScheduler")).contains( @@ -102,7 +102,7 @@ public class EnableSchedulingTests { ctx = new AnnotationConfigApplicationContext(ExplicitScheduledTaskRegistrarConfig.class); assertThat(ctx.getBean(ScheduledTaskHolder.class).getScheduledTasks()).hasSize(1); - Thread.sleep(100); + Thread.sleep(110); assertThat(ctx.getBean(AtomicInteger.class).get()).isGreaterThanOrEqualTo(10); assertThat(ctx.getBean(ExplicitScheduledTaskRegistrarConfig.class).threadName).startsWith("explicitScheduler1"); } @@ -124,7 +124,7 @@ public class EnableSchedulingTests { ctx = new AnnotationConfigApplicationContext( SchedulingEnabled_withAmbiguousTaskSchedulers_andSingleTask_disambiguatedByScheduledTaskRegistrar.class); - Thread.sleep(100); + Thread.sleep(110); assertThat(ctx.getBean(ThreadAwareWorker.class).executedByThread).startsWith("explicitScheduler2-"); } @@ -134,7 +134,7 @@ public class EnableSchedulingTests { ctx = new AnnotationConfigApplicationContext( SchedulingEnabled_withAmbiguousTaskSchedulers_andSingleTask_disambiguatedBySchedulerNameAttribute.class); - Thread.sleep(100); + Thread.sleep(110); assertThat(ctx.getBean(ThreadAwareWorker.class).executedByThread).startsWith("explicitScheduler2-"); } @@ -143,7 +143,7 @@ public class EnableSchedulingTests { public void withTaskAddedVia_configureTasks() throws InterruptedException { ctx = new AnnotationConfigApplicationContext(SchedulingEnabled_withTaskAddedVia_configureTasks.class); - Thread.sleep(100); + Thread.sleep(110); assertThat(ctx.getBean(ThreadAwareWorker.class).executedByThread).startsWith("taskScheduler-"); } @@ -152,7 +152,7 @@ public class EnableSchedulingTests { public void withTriggerTask() throws InterruptedException { ctx = new AnnotationConfigApplicationContext(TriggerTaskConfig.class); - Thread.sleep(100); + Thread.sleep(110); assertThat(ctx.getBean(AtomicInteger.class).get()).isGreaterThan(1); } diff --git a/spring-r2dbc/src/main/java/org/springframework/r2dbc/connection/ConnectionFactoryUtils.java b/spring-r2dbc/src/main/java/org/springframework/r2dbc/connection/ConnectionFactoryUtils.java index c69dc2f49e5..33a9b6bf823 100644 --- a/spring-r2dbc/src/main/java/org/springframework/r2dbc/connection/ConnectionFactoryUtils.java +++ b/spring-r2dbc/src/main/java/org/springframework/r2dbc/connection/ConnectionFactoryUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 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. @@ -85,7 +85,7 @@ public abstract class ConnectionFactoryUtils { */ public static Mono getConnection(ConnectionFactory connectionFactory) { return doGetConnection(connectionFactory) - .onErrorMap(e -> new DataAccessResourceFailureException("Failed to obtain R2DBC Connection", e)); + .onErrorMap(ex -> new DataAccessResourceFailureException("Failed to obtain R2DBC Connection", ex)); } /** @@ -124,17 +124,17 @@ public abstract class ConnectionFactoryUtils { holderToUse.setConnection(conn); } holderToUse.requested(); - synchronizationManager - .registerSynchronization(new ConnectionSynchronization(holderToUse, connectionFactory)); + synchronizationManager.registerSynchronization( + new ConnectionSynchronization(holderToUse, connectionFactory)); holderToUse.setSynchronizedWithTransaction(true); if (holderToUse != conHolder) { synchronizationManager.bindResource(connectionFactory, holderToUse); } }) // Unexpected exception from external delegation call -> close Connection and rethrow. - .onErrorResume(e -> releaseConnection(connection, connectionFactory).then(Mono.error(e)))); + .onErrorResume(ex -> releaseConnection(connection, connectionFactory).then(Mono.error(ex)))); } return con; - }).onErrorResume(NoTransactionException.class, e -> Mono.from(connectionFactory.create())); + }).onErrorResume(NoTransactionException.class, ex -> Mono.from(connectionFactory.create())); } /** @@ -159,7 +159,7 @@ public abstract class ConnectionFactoryUtils { */ public static Mono releaseConnection(Connection con, ConnectionFactory connectionFactory) { return doReleaseConnection(con, connectionFactory) - .onErrorMap(e -> new DataAccessResourceFailureException("Failed to close R2DBC Connection", e)); + .onErrorMap(ex -> new DataAccessResourceFailureException("Failed to close R2DBC Connection", ex)); } /** @@ -171,15 +171,14 @@ public abstract class ConnectionFactoryUtils { * @see #doGetConnection */ public static Mono doReleaseConnection(Connection connection, ConnectionFactory connectionFactory) { - return TransactionSynchronizationManager.forCurrentTransaction() - .flatMap(synchronizationManager -> { + return TransactionSynchronizationManager.forCurrentTransaction().flatMap(synchronizationManager -> { ConnectionHolder conHolder = (ConnectionHolder) synchronizationManager.getResource(connectionFactory); if (conHolder != null && connectionEquals(conHolder, connection)) { // It's the transactional Connection: Don't close it. conHolder.released(); } return Mono.from(connection.close()); - }).onErrorResume(NoTransactionException.class, e -> Mono.from(connection.close())); + }).onErrorResume(NoTransactionException.class, ex -> Mono.from(connection.close())); } /** @@ -268,7 +267,8 @@ public abstract class ConnectionFactoryUtils { Connection heldCon = conHolder.getConnection(); // Explicitly check for identity too: for Connection handles that do not implement // "equals" properly). - return (heldCon == passedInCon || heldCon.equals(passedInCon) || getTargetConnection(heldCon).equals(passedInCon)); + return (heldCon == passedInCon || heldCon.equals(passedInCon) || + getTargetConnection(heldCon).equals(passedInCon)); } /** diff --git a/spring-tx/src/main/java/org/springframework/transaction/reactive/TransactionContextManager.java b/spring-tx/src/main/java/org/springframework/transaction/reactive/TransactionContextManager.java index db6f4fb414c..cac1f04133c 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/reactive/TransactionContextManager.java +++ b/spring-tx/src/main/java/org/springframework/transaction/reactive/TransactionContextManager.java @@ -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. @@ -46,10 +46,10 @@ public abstract class TransactionContextManager { * transactional context holder. Context retrieval fails with NoTransactionException * if no context or context holder is registered. * @return the current {@link TransactionContext} - * @throws NoTransactionException if no TransactionContext was found in the subscriber context - * or no context found in a holder + * @throws NoTransactionException if no TransactionContext was found in the + * subscriber context or no context found in a holder */ - public static Mono currentContext() throws NoTransactionException { + public static Mono currentContext() { return Mono.deferContextual(ctx -> { if (ctx.hasKey(TransactionContext.class)) { return Mono.just(ctx.get(TransactionContext.class)); diff --git a/spring-tx/src/main/java/org/springframework/transaction/reactive/TransactionSynchronizationManager.java b/spring-tx/src/main/java/org/springframework/transaction/reactive/TransactionSynchronizationManager.java index 54f7cd17a03..27ed7f41739 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/reactive/TransactionSynchronizationManager.java +++ b/spring-tx/src/main/java/org/springframework/transaction/reactive/TransactionSynchronizationManager.java @@ -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. @@ -32,8 +32,8 @@ import org.springframework.util.Assert; /** * Central delegate that manages resources and transaction synchronizations per - * subscriber context. - * To be used by resource management code but not by typical application code. + * subscriber context. To be used by resource management code but not by typical + * application code. * *

Supports one resource per key without overwriting, that is, a resource needs * to be removed before a new one can be set for the same key. @@ -73,6 +73,7 @@ public class TransactionSynchronizationManager { public TransactionSynchronizationManager(TransactionContext transactionContext) { + Assert.notNull(transactionContext, "TransactionContext must not be null"); this.transactionContext = transactionContext; } @@ -88,10 +89,11 @@ public class TransactionSynchronizationManager { return TransactionContextManager.currentContext().map(TransactionSynchronizationManager::new); } + /** - * Check if there is a resource for the given key bound to the current thread. + * Check if there is a resource for the given key bound to the current context. * @param key the key to check (usually the resource factory) - * @return if there is a value bound to the current thread + * @return if there is a value bound to the current context */ public boolean hasResource(Object key) { Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key); @@ -100,9 +102,9 @@ public class TransactionSynchronizationManager { } /** - * Retrieve a resource for the given key that is bound to the current thread. + * Retrieve a resource for the given key that is bound to the current context. * @param key the key to check (usually the resource factory) - * @return a value bound to the current thread (usually the active + * @return a value bound to the current context (usually the active * resource object), or {@code null} if none */ @Nullable diff --git a/spring-tx/src/test/java/org/springframework/transaction/event/TransactionalEventListenerTests.java b/spring-tx/src/test/java/org/springframework/transaction/event/TransactionalEventListenerTests.java index 22ed99a2b9a..ff215f05133 100644 --- a/spring-tx/src/test/java/org/springframework/transaction/event/TransactionalEventListenerTests.java +++ b/spring-tx/src/test/java/org/springframework/transaction/event/TransactionalEventListenerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 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. @@ -57,6 +57,7 @@ import static org.springframework.transaction.event.TransactionPhase.BEFORE_COMM /** * Integration tests for {@link TransactionalEventListener} support + * with thread-bound transactions. * * @author Stephane Nicoll * @author Sam Brannen @@ -87,7 +88,6 @@ public class TransactionalEventListenerTests { getEventCollector().assertEvents(EventCollector.IMMEDIATELY, "test"); getEventCollector().assertTotalEventsCount(1); return null; - }); getEventCollector().assertEvents(EventCollector.IMMEDIATELY, "test"); getEventCollector().assertTotalEventsCount(1); @@ -115,7 +115,6 @@ public class TransactionalEventListenerTests { getContext().publishEvent("test"); getEventCollector().assertNoEventReceived(); return null; - }); getEventCollector().assertEvents(EventCollector.AFTER_COMPLETION, "test"); getEventCollector().assertTotalEventsCount(1); // After rollback not invoked @@ -129,7 +128,6 @@ public class TransactionalEventListenerTests { getEventCollector().assertNoEventReceived(); status.setRollbackOnly(); return null; - }); getEventCollector().assertEvents(EventCollector.AFTER_COMPLETION, "test"); getEventCollector().assertTotalEventsCount(1); // After rollback not invoked @@ -142,7 +140,6 @@ public class TransactionalEventListenerTests { getContext().publishEvent("test"); getEventCollector().assertNoEventReceived(); return null; - }); getEventCollector().assertEvents(EventCollector.AFTER_COMMIT, "test"); getEventCollector().assertTotalEventsCount(1); // After rollback not invoked @@ -307,13 +304,12 @@ public class TransactionalEventListenerTests { } @Test - public void afterCommitMetaAnnotation() throws Exception { + public void afterCommitMetaAnnotation() { load(AfterCommitMetaAnnotationTestListener.class); this.transactionTemplate.execute(status -> { getContext().publishEvent("test"); getEventCollector().assertNoEventReceived(); return null; - }); getEventCollector().assertEvents(EventCollector.AFTER_COMMIT, "test"); getEventCollector().assertTotalEventsCount(1); @@ -326,7 +322,6 @@ public class TransactionalEventListenerTests { getContext().publishEvent("SKIP"); getEventCollector().assertNoEventReceived(); return null; - }); getEventCollector().assertNoEventReceived(); } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/MappedInterceptor.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/MappedInterceptor.java index 2467605c77f..d7abc59b19d 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/MappedInterceptor.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/MappedInterceptor.java @@ -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. @@ -61,7 +61,7 @@ import org.springframework.web.util.pattern.PatternParseException; */ public final class MappedInterceptor implements HandlerInterceptor { - private static PathMatcher defaultPathMatcher = new AntPathMatcher(); + private static final PathMatcher defaultPathMatcher = new AntPathMatcher(); @Nullable