Skip to content

Commit fc091f7

Browse files
committed
Introduce @BatchTaskExecutor for customizing Batch's task executor
Closes gh-40040
1 parent a391613 commit fc091f7

File tree

4 files changed

+98
-1
lines changed

4 files changed

+98
-1
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfiguration.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import org.springframework.context.annotation.Configuration;
4545
import org.springframework.context.annotation.Import;
4646
import org.springframework.core.convert.support.ConfigurableConversionService;
47+
import org.springframework.core.task.TaskExecutor;
4748
import org.springframework.jdbc.datasource.init.DatabasePopulator;
4849
import org.springframework.transaction.PlatformTransactionManager;
4950
import org.springframework.transaction.annotation.Isolation;
@@ -101,6 +102,8 @@ static class SpringBootBatchConfiguration extends DefaultBatchConfiguration {
101102

102103
private final PlatformTransactionManager transactionManager;
103104

105+
private final TaskExecutor taskExector;
106+
104107
private final BatchProperties properties;
105108

106109
private final List<BatchConversionServiceCustomizer> batchConversionServiceCustomizers;
@@ -110,11 +113,12 @@ static class SpringBootBatchConfiguration extends DefaultBatchConfiguration {
110113
SpringBootBatchConfiguration(DataSource dataSource, @BatchDataSource ObjectProvider<DataSource> batchDataSource,
111114
PlatformTransactionManager transactionManager,
112115
@BatchTransactionManager ObjectProvider<PlatformTransactionManager> batchTransactionManager,
113-
BatchProperties properties,
116+
@BatchTaskExecutor ObjectProvider<TaskExecutor> batchTaskExecutor, BatchProperties properties,
114117
ObjectProvider<BatchConversionServiceCustomizer> batchConversionServiceCustomizers,
115118
ObjectProvider<ExecutionContextSerializer> executionContextSerializer) {
116119
this.dataSource = batchDataSource.getIfAvailable(() -> dataSource);
117120
this.transactionManager = batchTransactionManager.getIfAvailable(() -> transactionManager);
121+
this.taskExector = batchTaskExecutor.getIfAvailable();
118122
this.properties = properties;
119123
this.batchConversionServiceCustomizers = batchConversionServiceCustomizers.orderedStream().toList();
120124
this.executionContextSerializer = executionContextSerializer.getIfAvailable();
@@ -157,6 +161,11 @@ protected ExecutionContextSerializer getExecutionContextSerializer() {
157161
: super.getExecutionContextSerializer();
158162
}
159163

164+
@Override
165+
protected TaskExecutor getTaskExecutor() {
166+
return (this.taskExector != null) ? this.taskExector : super.getTaskExecutor();
167+
}
168+
160169
}
161170

162171
@Configuration(proxyBeanMethods = false)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright 2012-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure.batch;
18+
19+
import java.lang.annotation.Documented;
20+
import java.lang.annotation.ElementType;
21+
import java.lang.annotation.Retention;
22+
import java.lang.annotation.RetentionPolicy;
23+
import java.lang.annotation.Target;
24+
25+
import org.springframework.beans.factory.annotation.Qualifier;
26+
import org.springframework.context.annotation.Primary;
27+
import org.springframework.core.task.TaskExecutor;
28+
29+
/**
30+
* Qualifier annotation for a {@link TaskExecutor} to be injected into Batch
31+
* auto-configuration. Can be used on a secondary task executor source, if there is
32+
* another one marked as {@link Primary @Primary}.
33+
*
34+
* @author Andy Wilkinson
35+
* @since 3.4.0
36+
*/
37+
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE })
38+
@Retention(RetentionPolicy.RUNTIME)
39+
@Documented
40+
@Qualifier
41+
public @interface BatchTaskExecutor {
42+
43+
}

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfigurationTests.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@
7878
import org.springframework.context.annotation.Primary;
7979
import org.springframework.core.annotation.Order;
8080
import org.springframework.core.convert.support.ConfigurableConversionService;
81+
import org.springframework.core.task.AsyncTaskExecutor;
82+
import org.springframework.core.task.SimpleAsyncTaskExecutor;
83+
import org.springframework.core.task.SyncTaskExecutor;
84+
import org.springframework.core.task.TaskExecutor;
8185
import org.springframework.jdbc.BadSqlGrammarException;
8286
import org.springframework.jdbc.core.JdbcTemplate;
8387
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
@@ -361,6 +365,22 @@ void testBatchTransactionManager() {
361365
});
362366
}
363367

368+
@Test
369+
void testBatchTaskExecutor() {
370+
this.contextRunner
371+
.withUserConfiguration(TestConfiguration.class, BatchTaskExecutorConfiguration.class,
372+
EmbeddedDataSourceConfiguration.class)
373+
.run((context) -> {
374+
assertThat(context).hasSingleBean(SpringBootBatchConfiguration.class).hasBean("batchTaskExecutor");
375+
TaskExecutor batchTaskExecutor = context.getBean("batchTaskExecutor", TaskExecutor.class);
376+
assertThat(batchTaskExecutor).isInstanceOf(AsyncTaskExecutor.class);
377+
assertThat(context.getBean(SpringBootBatchConfiguration.class).getTaskExecutor())
378+
.isEqualTo(batchTaskExecutor);
379+
assertThat(context.getBean(JobLauncher.class)).hasFieldOrPropertyWithValue("taskExecutor",
380+
batchTaskExecutor);
381+
});
382+
}
383+
364384
@Test
365385
void jobRepositoryBeansDependOnBatchDataSourceInitializer() {
366386
this.contextRunner.withUserConfiguration(TestConfiguration.class, EmbeddedDataSourceConfiguration.class)
@@ -551,6 +571,23 @@ PlatformTransactionManager batchTransactionManager() {
551571

552572
}
553573

574+
@Configuration(proxyBeanMethods = false)
575+
static class BatchTaskExecutorConfiguration {
576+
577+
@Bean
578+
@Primary
579+
TaskExecutor taskExecutor() {
580+
return new SyncTaskExecutor();
581+
}
582+
583+
@Bean
584+
@BatchTaskExecutor
585+
TaskExecutor batchTaskExecutor() {
586+
return new SimpleAsyncTaskExecutor();
587+
}
588+
589+
}
590+
554591
@Configuration(proxyBeanMethods = false)
555592
static class EmptyConfiguration {
556593

spring-boot-project/spring-boot-docs/src/docs/antora/modules/how-to/pages/batch.adoc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@ If you do so and want two transaction managers, remember to mark the other one a
2929

3030

3131

32+
[[howto.batch.specifying-a-task-executor]]
33+
== Specifying a Batch Task Executor
34+
35+
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`.
36+
If you do so and want two task executors, remember to mark the other one as `@Primary`.
37+
38+
39+
3240
[[howto.batch.running-jobs-on-startup]]
3341
== Running Spring Batch Jobs on Startup
3442

0 commit comments

Comments
 (0)