Sophisticated lifecycle support for ThreadPoolTaskExecutor/Scheduler

Pause/resume capability through SmartLifecycle implementation.
ContextClosedEvent triggers early executor/scheduler shutdown.
Programmatic initiateShutdown() option for custom early shutdown.

Closes gh-30831
Closes gh-27090
Closes gh-24497
This commit is contained in:
Juergen Hoeller
2023-07-07 13:04:52 +02:00
parent 029a69f5c1
commit b12115b61f
7 changed files with 308 additions and 38 deletions

View File

@@ -26,6 +26,7 @@ import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.awaitility.Awaitility;
import org.junit.jupiter.api.Test;
@@ -96,9 +97,8 @@ public class EnableAsyncTests {
public void properExceptionForExistingProxyDependencyMismatch() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(AsyncConfig.class, AsyncBeanWithInterface.class, AsyncBeanUser.class);
assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(
ctx::refresh)
.withCauseInstanceOf(BeanNotOfRequiredTypeException.class);
assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(ctx::refresh)
.withCauseInstanceOf(BeanNotOfRequiredTypeException.class);
ctx.close();
}
@@ -106,9 +106,8 @@ public class EnableAsyncTests {
public void properExceptionForResolvedProxyDependencyMismatch() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(AsyncConfig.class, AsyncBeanUser.class, AsyncBeanWithInterface.class);
assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(
ctx::refresh)
.withCauseInstanceOf(BeanNotOfRequiredTypeException.class);
assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(ctx::refresh)
.withCauseInstanceOf(BeanNotOfRequiredTypeException.class);
ctx.close();
}
@@ -142,13 +141,18 @@ public class EnableAsyncTests {
context.getBean(AsyncBeanWithExecutorQualifiedByExpressionOrPlaceholder.class);
Future<Thread> workerThread1 = asyncBean.myWork1();
assertThat(workerThread1.get().getName()).startsWith("myExecutor1-");
assertThat(workerThread1.get(100, TimeUnit.MILLISECONDS).getName()).startsWith("myExecutor1-");
context.stop();
Future<Thread> workerThread2 = asyncBean.myWork2();
assertThat(workerThread2.get().getName()).startsWith("myExecutor2-");
assertThatExceptionOfType(TimeoutException.class).isThrownBy(
() -> workerThread2.get(100, TimeUnit.MILLISECONDS));
context.start();
assertThat(workerThread2.get(100, TimeUnit.MILLISECONDS).getName()).startsWith("myExecutor2-");
Future<Thread> workerThread3 = asyncBean.fallBackToDefaultExecutor();
assertThat(workerThread3.get().getName()).startsWith("SimpleAsyncTaskExecutor");
assertThat(workerThread3.get(100, TimeUnit.MILLISECONDS).getName()).startsWith("SimpleAsyncTaskExecutor");
}
finally {
System.clearProperty("myExecutor");
@@ -208,8 +212,7 @@ public class EnableAsyncTests {
@SuppressWarnings("resource")
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(AspectJAsyncAnnotationConfig.class);
assertThatExceptionOfType(BeanDefinitionStoreException.class).isThrownBy(
ctx::refresh);
assertThatExceptionOfType(BeanDefinitionStoreException.class).isThrownBy(ctx::refresh);
}
@Test
@@ -521,6 +524,7 @@ public class EnableAsyncTests {
}
}
@Configuration
@EnableAsync
static class AsyncWithExecutorQualifiedByExpressionConfig {

View File

@@ -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.
@@ -86,10 +86,20 @@ public class EnableSchedulingTests {
assertThat(ctx.getBean(ScheduledTaskHolder.class).getScheduledTasks()).hasSize(1);
Thread.sleep(100);
assertThat(ctx.getBean(AtomicInteger.class).get()).isGreaterThanOrEqualTo(10);
ctx.stop();
int count1 = ctx.getBean(AtomicInteger.class).get();
assertThat(count1).isGreaterThanOrEqualTo(10);
Thread.sleep(100);
int count2 = ctx.getBean(AtomicInteger.class).get();
assertThat(count2).isEqualTo(count1);
ctx.start();
Thread.sleep(100);
int count3 = ctx.getBean(AtomicInteger.class).get();
assertThat(count3).isGreaterThanOrEqualTo(20);
assertThat(ctx.getBean(ExplicitSchedulerConfig.class).threadName).startsWith("explicitScheduler-");
assertThat(Arrays.asList(ctx.getDefaultListableBeanFactory().getDependentBeans("myTaskScheduler")).contains(
TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)).isTrue();
TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)).isTrue();
}
@Test