From fc091f7bddc827eda60942adf5970be0751e5c52 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 22 Oct 2024 10:18:55 +0100 Subject: [PATCH] Introduce @BatchTaskExecutor for customizing Batch's task executor Closes gh-40040 --- .../batch/BatchAutoConfiguration.java | 11 ++++- .../batch/BatchTaskExecutor.java | 43 +++++++++++++++++++ .../batch/BatchAutoConfigurationTests.java | 37 ++++++++++++++++ .../antora/modules/how-to/pages/batch.adoc | 8 ++++ 4 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BatchTaskExecutor.java diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfiguration.java index 8635c1f9c5..c37293f317 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfiguration.java @@ -44,6 +44,7 @@ import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.core.convert.support.ConfigurableConversionService; +import org.springframework.core.task.TaskExecutor; import org.springframework.jdbc.datasource.init.DatabasePopulator; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.Isolation; @@ -101,6 +102,8 @@ public class BatchAutoConfiguration { private final PlatformTransactionManager transactionManager; + private final TaskExecutor taskExector; + private final BatchProperties properties; private final List batchConversionServiceCustomizers; @@ -110,11 +113,12 @@ public class BatchAutoConfiguration { SpringBootBatchConfiguration(DataSource dataSource, @BatchDataSource ObjectProvider batchDataSource, PlatformTransactionManager transactionManager, @BatchTransactionManager ObjectProvider batchTransactionManager, - BatchProperties properties, + @BatchTaskExecutor ObjectProvider batchTaskExecutor, BatchProperties properties, ObjectProvider batchConversionServiceCustomizers, ObjectProvider executionContextSerializer) { this.dataSource = batchDataSource.getIfAvailable(() -> dataSource); this.transactionManager = batchTransactionManager.getIfAvailable(() -> transactionManager); + this.taskExector = batchTaskExecutor.getIfAvailable(); this.properties = properties; this.batchConversionServiceCustomizers = batchConversionServiceCustomizers.orderedStream().toList(); this.executionContextSerializer = executionContextSerializer.getIfAvailable(); @@ -157,6 +161,11 @@ public class BatchAutoConfiguration { : super.getExecutionContextSerializer(); } + @Override + protected TaskExecutor getTaskExecutor() { + return (this.taskExector != null) ? this.taskExector : super.getTaskExecutor(); + } + } @Configuration(proxyBeanMethods = false) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BatchTaskExecutor.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BatchTaskExecutor.java new file mode 100644 index 0000000000..4a623125ab --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BatchTaskExecutor.java @@ -0,0 +1,43 @@ +/* + * Copyright 2012-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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.batch; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Primary; +import org.springframework.core.task.TaskExecutor; + +/** + * Qualifier annotation for a {@link TaskExecutor} to be injected into Batch + * auto-configuration. Can be used on a secondary task executor source, if there is + * another one marked as {@link Primary @Primary}. + * + * @author Andy Wilkinson + * @since 3.4.0 + */ +@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Qualifier +public @interface BatchTaskExecutor { + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfigurationTests.java index bf8bad0de8..1121e8fc84 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfigurationTests.java @@ -78,6 +78,10 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.core.annotation.Order; import org.springframework.core.convert.support.ConfigurableConversionService; +import org.springframework.core.task.AsyncTaskExecutor; +import org.springframework.core.task.SimpleAsyncTaskExecutor; +import org.springframework.core.task.SyncTaskExecutor; +import org.springframework.core.task.TaskExecutor; import org.springframework.jdbc.BadSqlGrammarException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; @@ -361,6 +365,22 @@ class BatchAutoConfigurationTests { }); } + @Test + void testBatchTaskExecutor() { + this.contextRunner + .withUserConfiguration(TestConfiguration.class, BatchTaskExecutorConfiguration.class, + EmbeddedDataSourceConfiguration.class) + .run((context) -> { + assertThat(context).hasSingleBean(SpringBootBatchConfiguration.class).hasBean("batchTaskExecutor"); + TaskExecutor batchTaskExecutor = context.getBean("batchTaskExecutor", TaskExecutor.class); + assertThat(batchTaskExecutor).isInstanceOf(AsyncTaskExecutor.class); + assertThat(context.getBean(SpringBootBatchConfiguration.class).getTaskExecutor()) + .isEqualTo(batchTaskExecutor); + assertThat(context.getBean(JobLauncher.class)).hasFieldOrPropertyWithValue("taskExecutor", + batchTaskExecutor); + }); + } + @Test void jobRepositoryBeansDependOnBatchDataSourceInitializer() { this.contextRunner.withUserConfiguration(TestConfiguration.class, EmbeddedDataSourceConfiguration.class) @@ -551,6 +571,23 @@ class BatchAutoConfigurationTests { } + @Configuration(proxyBeanMethods = false) + static class BatchTaskExecutorConfiguration { + + @Bean + @Primary + TaskExecutor taskExecutor() { + return new SyncTaskExecutor(); + } + + @Bean + @BatchTaskExecutor + TaskExecutor batchTaskExecutor() { + return new SimpleAsyncTaskExecutor(); + } + + } + @Configuration(proxyBeanMethods = false) static class EmptyConfiguration { diff --git a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/how-to/pages/batch.adoc b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/how-to/pages/batch.adoc index 202f11a2c2..a682256327 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/how-to/pages/batch.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/how-to/pages/batch.adoc @@ -29,6 +29,14 @@ If you do so and want two transaction managers, remember to mark the other one a +[[howto.batch.specifying-a-task-executor]] +== Specifying a Batch Task Executor + +Similar to xref:batch.adoc#howto.batch.specifying-a-data-source[], you can define a `TaskExecutor` for use in the batch processing by marking it as `@BatchTaskExecutor`. +If you do so and want two task executors, remember to mark the other one as `@Primary`. + + + [[howto.batch.running-jobs-on-startup]] == Running Spring Batch Jobs on Startup