TaskExecutionAutoConfigurationTests Class — spring-boot Architecture
Architecture documentation for the TaskExecutionAutoConfigurationTests class in TaskExecutionAutoConfigurationTests.java from the spring-boot codebase.
Entity Profile
Relationship Graph
Source Code
core/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/task/TaskExecutionAutoConfigurationTests.java lines 79–721
@ExtendWith(OutputCaptureExtension.class)
class TaskExecutionAutoConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(TaskExecutionAutoConfiguration.class));
@Test
void shouldSupplyBeans() {
this.contextRunner.run((context) -> {
assertThat(context).hasSingleBean(ThreadPoolTaskExecutorBuilder.class);
assertThat(context).hasSingleBean(ThreadPoolTaskExecutor.class);
assertThat(context).hasSingleBean(SimpleAsyncTaskExecutorBuilder.class);
});
}
@Test
void simpleAsyncTaskExecutorBuilderShouldReadProperties() {
this.contextRunner
.withPropertyValues("spring.task.execution.thread-name-prefix=mytest-",
"spring.task.execution.simple.cancel-remaining-tasks-on-close=true",
"spring.task.execution.simple.reject-tasks-when-limit-reached=true",
"spring.task.execution.simple.concurrency-limit=1",
"spring.task.execution.shutdown.await-termination=true",
"spring.task.execution.shutdown.await-termination-period=30s")
.run(assertSimpleAsyncTaskExecutor((taskExecutor) -> {
assertThat(taskExecutor).hasFieldOrPropertyWithValue("cancelRemainingTasksOnClose", true);
assertThat(taskExecutor).hasFieldOrPropertyWithValue("rejectTasksWhenLimitReached", true);
assertThat(taskExecutor.getConcurrencyLimit()).isEqualTo(1);
assertThat(taskExecutor.getThreadNamePrefix()).isEqualTo("mytest-");
assertThat(taskExecutor).hasFieldOrPropertyWithValue("taskTerminationTimeout", 30000L);
}));
}
@Test
void threadPoolTaskExecutorBuilderShouldApplyCustomSettings() {
this.contextRunner.withPropertyValues("spring.task.execution.pool.queue-capacity=10",
"spring.task.execution.pool.core-size=2", "spring.task.execution.pool.max-size=4",
"spring.task.execution.pool.allow-core-thread-timeout=true", "spring.task.execution.pool.keep-alive=5s",
"spring.task.execution.pool.shutdown.accept-tasks-after-context-close=true",
"spring.task.execution.shutdown.await-termination=true",
"spring.task.execution.shutdown.await-termination-period=30s",
"spring.task.execution.thread-name-prefix=mytest-")
.run(assertThreadPoolTaskExecutor((taskExecutor) -> {
assertThat(taskExecutor).hasFieldOrPropertyWithValue("queueCapacity", 10);
assertThat(taskExecutor.getCorePoolSize()).isEqualTo(2);
assertThat(taskExecutor.getMaxPoolSize()).isEqualTo(4);
assertThat(taskExecutor).hasFieldOrPropertyWithValue("allowCoreThreadTimeOut", true);
assertThat(taskExecutor.getKeepAliveSeconds()).isEqualTo(5);
assertThat(taskExecutor).hasFieldOrPropertyWithValue("acceptTasksAfterContextClose", true);
assertThat(taskExecutor).hasFieldOrPropertyWithValue("waitForTasksToCompleteOnShutdown", true);
assertThat(taskExecutor).hasFieldOrPropertyWithValue("awaitTerminationMillis", 30000L);
assertThat(taskExecutor.getThreadNamePrefix()).isEqualTo("mytest-");
}));
}
@Test
void threadPoolTaskExecutorBuilderWhenHasCustomBuilderShouldUseCustomBuilder() {
this.contextRunner.withUserConfiguration(CustomThreadPoolTaskExecutorBuilderConfig.class).run((context) -> {
assertThat(context).hasSingleBean(ThreadPoolTaskExecutorBuilder.class);
assertThat(context.getBean(ThreadPoolTaskExecutorBuilder.class))
.isSameAs(context.getBean(CustomThreadPoolTaskExecutorBuilderConfig.class).builder);
});
}
@Test
void threadPoolTaskExecutorBuilderShouldUseTaskDecorator() {
this.contextRunner.withBean(TaskDecorator.class, OrderedTaskDecorator::new).run((context) -> {
assertThat(context).hasSingleBean(ThreadPoolTaskExecutorBuilder.class);
ThreadPoolTaskExecutor executor = context.getBean(ThreadPoolTaskExecutorBuilder.class).build();
assertThat(executor).extracting("taskDecorator").isSameAs(context.getBean(TaskDecorator.class));
});
}
@Test
void threadPoolTaskExecutorBuilderShouldUseCompositeTaskDecorator() {
this.contextRunner.withBean("taskDecorator1", TaskDecorator.class, () -> new OrderedTaskDecorator(1))
.withBean("taskDecorator2", TaskDecorator.class, () -> new OrderedTaskDecorator(3))
.withBean("taskDecorator3", TaskDecorator.class, () -> new OrderedTaskDecorator(2))
.run((context) -> {
assertThat(context).hasSingleBean(ThreadPoolTaskExecutorBuilder.class);
ThreadPoolTaskExecutor executor = context.getBean(ThreadPoolTaskExecutorBuilder.class).build();
assertThat(executor).extracting("taskDecorator")
.isInstanceOf(CompositeTaskDecorator.class)
.extracting("taskDecorators")
.asInstanceOf(InstanceOfAssertFactories.list(TaskDecorator.class))
.containsExactly(context.getBean("taskDecorator1", TaskDecorator.class),
context.getBean("taskDecorator3", TaskDecorator.class),
context.getBean("taskDecorator2", TaskDecorator.class));
});
}
@Test
void whenThreadPoolTaskExecutorIsAutoConfiguredThenItIsLazy() {
this.contextRunner.run((context) -> {
assertThat(context).hasSingleBean(Executor.class).hasBean("applicationTaskExecutor");
BeanDefinition beanDefinition = context.getSourceApplicationContext()
.getBeanFactory()
.getBeanDefinition("applicationTaskExecutor");
assertThat(beanDefinition.isLazyInit()).isTrue();
assertThat(context).getBean("applicationTaskExecutor").isInstanceOf(ThreadPoolTaskExecutor.class);
});
}
@Test
@EnabledForJreRange(min = JRE.JAVA_21)
void whenVirtualThreadsAreEnabledThenSimpleAsyncTaskExecutorWithVirtualThreadsIsAutoConfigured() {
this.contextRunner.withPropertyValues("spring.threads.virtual.enabled=true").run((context) -> {
assertThat(context).hasSingleBean(Executor.class).hasBean("applicationTaskExecutor");
assertThat(context).getBean("applicationTaskExecutor").isInstanceOf(SimpleAsyncTaskExecutor.class);
SimpleAsyncTaskExecutor taskExecutor = context.getBean("applicationTaskExecutor",
SimpleAsyncTaskExecutor.class);
assertThat(virtualThreadName(taskExecutor)).startsWith("task-");
});
}
@Test
@EnabledForJreRange(min = JRE.JAVA_21)
void whenTaskNamePrefixIsConfiguredThenSimpleAsyncTaskExecutorWithVirtualThreadsUsesIt() {
this.contextRunner
.withPropertyValues("spring.threads.virtual.enabled=true",
"spring.task.execution.thread-name-prefix=custom-")
.run((context) -> {
SimpleAsyncTaskExecutor taskExecutor = context.getBean("applicationTaskExecutor",
SimpleAsyncTaskExecutor.class);
assertThat(virtualThreadName(taskExecutor)).startsWith("custom-");
});
}
@Test
@EnabledForJreRange(min = JRE.JAVA_21)
void whenVirtualThreadsAreAvailableButNotEnabledThenThreadPoolTaskExecutorIsAutoConfigured() {
this.contextRunner.run((context) -> {
assertThat(context).hasSingleBean(Executor.class).hasBean("applicationTaskExecutor");
assertThat(context).getBean("applicationTaskExecutor").isInstanceOf(ThreadPoolTaskExecutor.class);
});
}
@Test
@EnabledForJreRange(min = JRE.JAVA_21)
void whenTaskDecoratorIsDefinedThenSimpleAsyncTaskExecutorWithVirtualThreadsUsesIt() {
this.contextRunner.withPropertyValues("spring.threads.virtual.enabled=true")
.withBean(TaskDecorator.class, OrderedTaskDecorator::new)
.run((context) -> {
SimpleAsyncTaskExecutor executor = context.getBean(SimpleAsyncTaskExecutor.class);
assertThat(executor).extracting("taskDecorator").isSameAs(context.getBean(TaskDecorator.class));
});
}
@Test
@EnabledForJreRange(min = JRE.JAVA_21)
void whenTaskDecoratorsAreDefinedThenSimpleAsyncTaskExecutorWithVirtualThreadsUsesThem() {
this.contextRunner.withPropertyValues("spring.threads.virtual.enabled=true")
.withBean("taskDecorator1", TaskDecorator.class, () -> new OrderedTaskDecorator(1))
.withBean("taskDecorator2", TaskDecorator.class, () -> new OrderedTaskDecorator(3))
.withBean("taskDecorator3", TaskDecorator.class, () -> new OrderedTaskDecorator(2))
.run((context) -> {
SimpleAsyncTaskExecutor executor = context.getBean(SimpleAsyncTaskExecutor.class);
assertThat(executor).extracting("taskDecorator")
.isInstanceOf(CompositeTaskDecorator.class)
.extracting("taskDecorators")
.asInstanceOf(InstanceOfAssertFactories.list(TaskDecorator.class))
.containsExactly(context.getBean("taskDecorator1", TaskDecorator.class),
context.getBean("taskDecorator3", TaskDecorator.class),
context.getBean("taskDecorator2", TaskDecorator.class));
});
}
@Test
void simpleAsyncTaskExecutorBuilderUsesPlatformThreadsByDefault() {
this.contextRunner.run((context) -> {
SimpleAsyncTaskExecutorBuilder builder = context.getBean(SimpleAsyncTaskExecutorBuilder.class);
assertThat(builder).hasFieldOrPropertyWithValue("virtualThreads", null);
});
}
@Test
@EnabledForJreRange(min = JRE.JAVA_21)
void simpleAsyncTaskExecutorBuilderUsesVirtualThreadsWhenEnabled() {
this.contextRunner.withPropertyValues("spring.threads.virtual.enabled=true").run((context) -> {
SimpleAsyncTaskExecutorBuilder builder = context.getBean(SimpleAsyncTaskExecutorBuilder.class);
assertThat(builder).hasFieldOrPropertyWithValue("virtualThreads", true);
});
}
@Test
@WithResource(name = "META-INF/services/io.micrometer.context.ThreadLocalAccessor",
content = "org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfigurationTests$TestThreadLocalAccessor")
void asyncTaskExecutorShouldNotNotRegisterContextPropagatingTaskDecoratorByDefault() {
this.contextRunner.withUserConfiguration(AsyncConfiguration.class, TestBean.class).run((context) -> {
assertThat(context).doesNotHaveBean(ContextPropagatingTaskDecorator.class);
TestBean bean = context.getBean(TestBean.class);
TestThreadLocalHolder.setValue("from-context");
String text = bean.echoContext().get();
assertThat(text).contains("task-").endsWith("null");
});
}
@Test
@WithResource(name = "META-INF/services/io.micrometer.context.ThreadLocalAccessor",
content = "org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfigurationTests$TestThreadLocalAccessor")
void asyncTaskExecutorWhenContextPropagationIsEnabledShouldRegisterBean() {
this.contextRunner.withUserConfiguration(AsyncConfiguration.class, TestBean.class)
.withPropertyValues("spring.task.execution.propagate-context=true")
.run((context) -> {
assertThat(context).hasSingleBean(ContextPropagatingTaskDecorator.class);
TestBean bean = context.getBean(TestBean.class);
TestThreadLocalHolder.setValue("from-context");
String text = bean.echoContext().get();
assertThat(text).contains("task-").endsWith("from-context");
});
}
@Test
void taskExecutorWhenHasCustomTaskExecutorShouldBackOff() {
this.contextRunner.withBean("customTaskExecutor", Executor.class, SyncTaskExecutor::new).run((context) -> {
assertThat(context).hasSingleBean(Executor.class);
assertThat(context.getBean(Executor.class)).isSameAs(context.getBean("customTaskExecutor"));
});
}
@Test
void taskExecutorWhenModeIsAutoAndHasCustomTaskExecutorShouldBackOff() {
this.contextRunner.withBean("customTaskExecutor", Executor.class, SyncTaskExecutor::new)
.withPropertyValues("spring.task.execution.mode=auto")
.run((context) -> {
assertThat(context).hasSingleBean(Executor.class);
assertThat(context.getBean(Executor.class)).isSameAs(context.getBean("customTaskExecutor"));
});
}
@Test
void taskExecutorWhenModeIsForceAndHasCustomTaskExecutorShouldCreateApplicationTaskExecutor() {
this.contextRunner.withBean("customTaskExecutor", Executor.class, SyncTaskExecutor::new)
.withPropertyValues("spring.task.execution.mode=force")
.run((context) -> assertThat(context.getBeansOfType(Executor.class)).hasSize(2)
.containsKeys("customTaskExecutor", "applicationTaskExecutor"));
}
@Test
void taskExecutorWhenModeIsForceAndHasCustomTaskExecutorWithReservedNameShouldThrowException() {
this.contextRunner.withBean("applicationTaskExecutor", Executor.class, SyncTaskExecutor::new)
.withPropertyValues("spring.task.execution.mode=force")
.run((context) -> assertThat(context).hasFailed()
.getFailure()
.isInstanceOf(BeanDefinitionOverrideException.class));
}
@Test
void taskExecutorWhenModeIsForceAndHasCustomBFPPCanRestoreTaskExecutorAlias() {
this.contextRunner.withBean("customTaskExecutor", Executor.class, SyncTaskExecutor::new)
.withPropertyValues("spring.task.execution.mode=force")
.withBean(BeanFactoryPostProcessor.class,
() -> (beanFactory) -> beanFactory.registerAlias("applicationTaskExecutor", "taskExecutor"))
.run((context) -> {
assertThat(context.getBeansOfType(Executor.class)).hasSize(2)
.containsKeys("customTaskExecutor", "applicationTaskExecutor");
assertThat(context).hasBean("taskExecutor");
assertThat(context.getBean("taskExecutor")).isSameAs(context.getBean("applicationTaskExecutor"));
});
}
@Test
@EnabledForJreRange(min = JRE.JAVA_21)
void whenVirtualThreadsAreEnabledAndCustomTaskExecutorIsDefinedThenSimpleAsyncTaskExecutorThatUsesVirtualThreadsBacksOff() {
this.contextRunner.withBean("customTaskExecutor", Executor.class, SyncTaskExecutor::new)
.withPropertyValues("spring.threads.virtual.enabled=true")
.run((context) -> {
assertThat(context).hasSingleBean(Executor.class);
assertThat(context.getBean(Executor.class)).isSameAs(context.getBean("customTaskExecutor"));
});
}
@Test
void enableAsyncUsesAutoConfiguredOneByDefault() {
this.contextRunner.withPropertyValues("spring.task.execution.thread-name-prefix=auto-task-")
.withUserConfiguration(AsyncConfiguration.class, TestBean.class)
.run((context) -> {
assertThat(context).hasSingleBean(AsyncConfigurer.class);
assertThat(context).hasSingleBean(TaskExecutor.class);
TestBean bean = context.getBean(TestBean.class);
String text = bean.echo("something").get();
assertThat(text).contains("auto-task-").contains("something");
});
}
@Test
void enableAsyncUsesCustomExecutorIfPresent() {
this.contextRunner.withPropertyValues("spring.task.execution.thread-name-prefix=auto-task-")
.withBean("customTaskExecutor", Executor.class, () -> createCustomAsyncExecutor("custom-task-"))
.withUserConfiguration(AsyncConfiguration.class, TestBean.class)
.run((context) -> {
assertThat(context).doesNotHaveBean(AsyncConfigurer.class);
assertThat(context).hasSingleBean(Executor.class);
TestBean bean = context.getBean(TestBean.class);
String text = bean.echo("something").get();
assertThat(text).contains("custom-task-").contains("something");
});
}
@Test
void enableAsyncUsesAutoConfiguredExecutorWhenModeIsForceAndHasCustomTaskExecutor() {
this.contextRunner
.withPropertyValues("spring.task.execution.thread-name-prefix=auto-task-",
"spring.task.execution.mode=force")
.withBean("customTaskExecutor", Executor.class, () -> createCustomAsyncExecutor("custom-task-"))
.withUserConfiguration(AsyncConfiguration.class, TestBean.class)
.run((context) -> {
assertThat(context).hasSingleBean(AsyncConfigurer.class);
assertThat(context.getBeansOfType(Executor.class)).hasSize(2);
TestBean bean = context.getBean(TestBean.class);
String text = bean.echo("something").get();
assertThat(text).contains("auto-task-").contains("something");
});
}
@Test
void enableAsyncUsesAutoConfiguredExecutorWhenModeIsForceAndHasCustomTaskExecutorWithReservedName() {
this.contextRunner
.withPropertyValues("spring.task.execution.thread-name-prefix=auto-task-",
"spring.task.execution.mode=force")
.withBean("taskExecutor", Executor.class, () -> createCustomAsyncExecutor("custom-task-"))
.withUserConfiguration(AsyncConfiguration.class, TestBean.class)
.run((context) -> {
assertThat(context).hasSingleBean(AsyncConfigurer.class);
assertThat(context.getBeansOfType(Executor.class)).hasSize(2);
TestBean bean = context.getBean(TestBean.class);
String text = bean.echo("something").get();
assertThat(text).contains("auto-task-").contains("something");
});
}
@Test
void enableAsyncUsesAsyncConfigurerWhenModeIsForce() {
this.contextRunner
.withPropertyValues("spring.task.execution.thread-name-prefix=auto-task-",
"spring.task.execution.mode=force")
.withBean("taskExecutor", Executor.class, () -> createCustomAsyncExecutor("custom-task-"))
.withBean("customAsyncConfigurer", AsyncConfigurer.class, () -> new AsyncConfigurer() {
@Override
public Executor getAsyncExecutor() {
return createCustomAsyncExecutor("async-task-");
}
})
.withUserConfiguration(AsyncConfiguration.class, TestBean.class)
.run((context) -> {
assertThat(context).hasSingleBean(AsyncConfigurer.class);
assertThat(context.getBeansOfType(Executor.class)).hasSize(2)
.containsOnlyKeys("taskExecutor", "applicationTaskExecutor");
TestBean bean = context.getBean(TestBean.class);
String text = bean.echo("something").get();
assertThat(text).contains("async-task-").contains("something");
});
}
@Test
void enableAsyncLinksToCustomTaskExecutorWhenAsyncConfigurerOverridesIt() {
Executor executor = createCustomAsyncExecutor("async-task-");
AsyncUncaughtExceptionHandler asyncUncaughtExceptionHandler = mock(AsyncUncaughtExceptionHandler.class);
this.contextRunner.withPropertyValues("spring.task.execution.thread-name-prefix=auto-task-")
.withBean("taskScheduler", TaskScheduler.class, () -> mock(ThreadPoolTaskScheduler.class))
.withBean("customAsyncConfigurer", AsyncConfigurer.class, () -> new AsyncConfigurer() {
@Override
public @Nullable Executor getAsyncExecutor() {
return executor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return asyncUncaughtExceptionHandler;
}
})
.withUserConfiguration(AsyncConfiguration.class, TestBean.class)
.run((context) -> {
assertThat(context).hasSingleBean(AsyncConfigurer.class);
assertThat(context.getBeansOfType(Executor.class)).containsOnlyKeys("applicationTaskExecutor",
"taskScheduler");
TestBean bean = context.getBean(TestBean.class);
String text = bean.echo("something").get();
assertThat(text).contains("async-task-").contains("something");
AsyncConfigurer asyncConfigurer = context.getBean(AsyncConfigurer.class);
assertThat(asyncConfigurer.getAsyncExecutor()).isEqualTo(executor);
assertThat(asyncConfigurer.getAsyncUncaughtExceptionHandler()).isEqualTo(asyncUncaughtExceptionHandler);
});
}
@Test
void enableAsyncLinksToApplicationTaskExecutorWhenAsyncConfigurerDoesNotOverrideIt() {
AsyncUncaughtExceptionHandler asyncUncaughtExceptionHandler = mock(AsyncUncaughtExceptionHandler.class);
this.contextRunner.withPropertyValues("spring.task.execution.thread-name-prefix=auto-task-")
.withBean("taskScheduler", TaskScheduler.class, () -> mock(ThreadPoolTaskScheduler.class))
.withBean("customAsyncConfigurer", AsyncConfigurer.class, () -> new AsyncConfigurer() {
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return asyncUncaughtExceptionHandler;
}
})
.withUserConfiguration(AsyncConfiguration.class, TestBean.class)
.run((context) -> {
assertThat(context).hasSingleBean(AsyncConfigurer.class);
assertThat(context.getBeansOfType(Executor.class)).containsOnlyKeys("applicationTaskExecutor",
"taskScheduler");
TestBean bean = context.getBean(TestBean.class);
String text = bean.echo("something").get();
assertThat(text).contains("auto-task-").contains("something");
assertThat(context.getBean(AsyncConfigurer.class).getAsyncUncaughtExceptionHandler())
.isEqualTo(asyncUncaughtExceptionHandler);
});
}
@Test
void enableAsyncUsesAutoConfiguredExecutorWhenModeIsForceAndHasPrimaryCustomTaskExecutor() {
this.contextRunner
.withPropertyValues("spring.task.execution.thread-name-prefix=auto-task-",
"spring.task.execution.mode=force")
.withBean("taskExecutor", Executor.class, () -> createCustomAsyncExecutor("custom-task-"),
(beanDefinition) -> beanDefinition.setPrimary(true))
.withUserConfiguration(AsyncConfiguration.class, TestBean.class)
.run((context) -> {
assertThat(context).hasSingleBean(AsyncConfigurer.class);
assertThat(context.getBeansOfType(Executor.class)).hasSize(2);
TestBean bean = context.getBean(TestBean.class);
String text = bean.echo("something").get();
assertThat(text).contains("auto-task-").contains("something");
});
}
@Test
void enableAsyncUsesAutoConfiguredOneByDefaultEvenThoughSchedulingIsConfigured() {
this.contextRunner.withPropertyValues("spring.task.execution.thread-name-prefix=auto-task-")
.withConfiguration(AutoConfigurations.of(TaskSchedulingAutoConfiguration.class))
.withUserConfiguration(AsyncConfiguration.class, SchedulingConfiguration.class, TestBean.class)
.run((context) -> {
TestBean bean = context.getBean(TestBean.class);
String text = bean.echo("something").get();
assertThat(text).contains("auto-task-").contains("something");
});
}
@Test
void shouldAliasApplicationTaskExecutorToBootstrapExecutor() {
this.contextRunner.run((context) -> {
assertThat(context).hasSingleBean(Executor.class)
.hasBean("applicationTaskExecutor")
.hasBean(ConfigurableApplicationContext.BOOTSTRAP_EXECUTOR_BEAN_NAME);
assertThat(context.getAliases("applicationTaskExecutor"))
.containsExactly(ConfigurableApplicationContext.BOOTSTRAP_EXECUTOR_BEAN_NAME);
assertThat(context.getBean(ConfigurableApplicationContext.BOOTSTRAP_EXECUTOR_BEAN_NAME))
.isSameAs(context.getBean("applicationTaskExecutor"));
});
}
@Test
void shouldNotAliasApplicationTaskExecutorWhenBootstrapExecutorIsDefined() {
this.contextRunner.withBean("applicationTaskExecutor", Executor.class, () -> createCustomAsyncExecutor("app-"))
.withBean(ConfigurableApplicationContext.BOOTSTRAP_EXECUTOR_BEAN_NAME, Executor.class,
() -> createCustomAsyncExecutor("bootstrap-"))
.run((context) -> {
assertThat(context.getBeansOfType(Executor.class)).hasSize(2);
assertThat(context).hasBean("applicationTaskExecutor")
.hasBean(ConfigurableApplicationContext.BOOTSTRAP_EXECUTOR_BEAN_NAME);
assertThat(context.getAliases("applicationTaskExecutor")).isEmpty();
assertThat(context.getBean(ConfigurableApplicationContext.BOOTSTRAP_EXECUTOR_BEAN_NAME))
.isNotSameAs(context.getBean("applicationTaskExecutor"));
});
}
@Test
void shouldNotAliasApplicationTaskExecutorWhenApplicationTaskExecutorIsMissing() {
this.contextRunner.withBean("customExecutor", Executor.class, () -> createCustomAsyncExecutor("custom-"))
.run((context) -> assertThat(context).hasSingleBean(Executor.class)
.hasBean("customExecutor")
.doesNotHaveBean("applicationTaskExecutor")
.doesNotHaveBean(ConfigurableApplicationContext.BOOTSTRAP_EXECUTOR_BEAN_NAME));
}
@Test
void shouldNotAliasApplicationTaskExecutorWhenBootstrapExecutorRegisteredAsSingleton() {
this.contextRunner.withBean("applicationTaskExecutor", Executor.class, () -> createCustomAsyncExecutor("app-"))
.withInitializer((context) -> context.getBeanFactory()
.registerSingleton(ConfigurableApplicationContext.BOOTSTRAP_EXECUTOR_BEAN_NAME,
createCustomAsyncExecutor("bootstrap-")))
.run((context) -> {
assertThat(context.getBeansOfType(Executor.class)).hasSize(2);
assertThat(context).hasBean("applicationTaskExecutor")
.hasBean(ConfigurableApplicationContext.BOOTSTRAP_EXECUTOR_BEAN_NAME);
assertThat(context.getAliases("applicationTaskExecutor")).isEmpty();
assertThat(context.getBean(ConfigurableApplicationContext.BOOTSTRAP_EXECUTOR_BEAN_NAME))
.isNotSameAs(context.getBean("applicationTaskExecutor"));
});
}
@Test
void shouldNotAliasApplicationTaskExecutorWhenBootstrapExecutorAliasIsDefined() {
Executor executor = Runnable::run;
this.contextRunner.withBean("applicationTaskExecutor", Executor.class, () -> executor)
.withBean("customExecutor", Executor.class, () -> createCustomAsyncExecutor("custom"))
.withInitializer((context) -> context.getBeanFactory()
.registerAlias("customExecutor", ConfigurableApplicationContext.BOOTSTRAP_EXECUTOR_BEAN_NAME))
.run((context) -> {
assertThat(context.getBeansOfType(Executor.class)).hasSize(2);
assertThat(context).hasBean("applicationTaskExecutor").hasBean("customExecutor");
assertThat(context.getAliases("applicationTaskExecutor")).isEmpty();
assertThat(context.getAliases("customExecutor"))
.contains(ConfigurableApplicationContext.BOOTSTRAP_EXECUTOR_BEAN_NAME);
assertThat(context.getBean(ConfigurableApplicationContext.BOOTSTRAP_EXECUTOR_BEAN_NAME))
.isNotSameAs(context.getBean("applicationTaskExecutor"))
.isSameAs(context.getBean("customExecutor"));
});
}
private Executor createCustomAsyncExecutor(String threadNamePrefix) {
SimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor();
executor.setThreadNamePrefix(threadNamePrefix);
return executor;
}
private ContextConsumer<AssertableApplicationContext> assertThreadPoolTaskExecutor(
Consumer<ThreadPoolTaskExecutor> taskExecutor) {
return (context) -> {
assertThat(context).hasSingleBean(ThreadPoolTaskExecutorBuilder.class);
ThreadPoolTaskExecutorBuilder builder = context.getBean(ThreadPoolTaskExecutorBuilder.class);
taskExecutor.accept(builder.build());
};
}
private ContextConsumer<AssertableApplicationContext> assertSimpleAsyncTaskExecutor(
Consumer<SimpleAsyncTaskExecutor> taskExecutor) {
return (context) -> {
assertThat(context).hasSingleBean(SimpleAsyncTaskExecutorBuilder.class);
SimpleAsyncTaskExecutorBuilder builder = context.getBean(SimpleAsyncTaskExecutorBuilder.class);
taskExecutor.accept(builder.build());
};
}
private String virtualThreadName(SimpleAsyncTaskExecutor taskExecutor) throws InterruptedException {
AtomicReference<Thread> threadReference = new AtomicReference<>();
CountDownLatch latch = new CountDownLatch(1);
taskExecutor.execute(() -> {
Thread currentThread = Thread.currentThread();
threadReference.set(currentThread);
latch.countDown();
});
assertThat(latch.await(30, TimeUnit.SECONDS)).isTrue();
Thread thread = threadReference.get();
assertThat(thread).isNotNull();
assertThat(thread).extracting("virtual").as("%s is virtual", thread).isEqualTo(true);
return thread.getName();
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@WithResource(name = "META-INF/services/io.micrometer.context.ThreadLocalAccessor",
content = "org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfigurationTests.TestThreadLocalAccessor")
@interface WithThreadLocalAccessor {
}
@Configuration(proxyBeanMethods = false)
static class CustomThreadPoolTaskExecutorBuilderConfig {
private final ThreadPoolTaskExecutorBuilder builder = new ThreadPoolTaskExecutorBuilder();
@Bean
ThreadPoolTaskExecutorBuilder customThreadPoolTaskExecutorBuilder() {
return this.builder;
}
}
@Configuration(proxyBeanMethods = false)
@EnableAsync
static class AsyncConfiguration {
}
@Configuration(proxyBeanMethods = false)
@EnableScheduling
static class SchedulingConfiguration {
}
static class TestBean {
@Async
Future<String> echo(String text) {
return CompletableFuture.completedFuture(Thread.currentThread().getName() + " " + text);
}
@Async
Future<String> echoContext() {
return CompletableFuture
.completedFuture(Thread.currentThread().getName() + " " + TestThreadLocalHolder.getValue());
}
}
static class TestThreadLocalHolder {
private static final ThreadLocal<String> holder = new ThreadLocal<>();
static void setValue(String value) {
holder.set(value);
}
static String getValue() {
return holder.get();
}
static void reset() {
holder.remove();
}
}
public static class TestThreadLocalAccessor implements ThreadLocalAccessor<String> {
static final String KEY = "test.threadlocal";
@Override
public Object key() {
return KEY;
}
@Override
public String getValue() {
return TestThreadLocalHolder.getValue();
}
@Override
public void setValue(String value) {
TestThreadLocalHolder.setValue(value);
}
@Override
public void setValue() {
TestThreadLocalHolder.reset();
}
}
}
Domain
Source
Analyze Your Own Codebase
Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.
Try Supermodel Free