I've been working for a few days on a Spring Batch (4.3.10) on Spring Cloud Task (2.4.6) all via Spring Boot (2.7.18). I'm going crazy trying to figure out how to configure my application and specifically how auto-configurations works...
What I don't understand is how to properly configure the same TransactionManager in JOB+TASK repositories.
I currently have this configurations (in summary):
DataSourceConfiguration
@Configuration
public class DataSourceConfiguration {
@BatchDataSource
@Bean
@ConfigurationProperties("batchandtask.datasource")
public DataSource batchDataSource() {
return batchDataSourceProperties().initializeDataSourceBuilder().build();
}
@Bean
public DataSourceProperties batchDataSourceProperties() {
return new DataSourceProperties();
}
@Bean
public TaskConfigurer taskConfigurer() {
return new DefaultTaskConfigurer(batchDataSource());
}
...others DataSource configs
Some notes on this configuration:
- I've used
@BatchDataSourceannotation because I have another@PrimaryDataSource, which I deliberately omitted because it is not strictly related to my doubts. - I defined a bean of type
TaskConfigurerbecause I want to use the same data source as the batch.
JobConfiguration
@Configuration
@EnableBatchProcessing
@EnableTask
public class JobConfiguration {
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
public JobConfiguration(JobBuilderFactory jobBuilderFactory,
StepBuilderFactory stepBuilderFactory) {
this.jobBuilderFactory = jobBuilderFactory;
this.stepBuilderFactory = stepBuilderFactory;
}
... Job and Steps beans
Auto-configurations
From what I saw in the code, the auto-configurations of the task occur before those of the batch and specifically this bean is created during this auto-config:
public class SimpleTaskAutoConfiguration {
...
@Bean
public PlatformTransactionManager springCloudTaskTransactionManager() {
return this.platformTransactionManager;
}
So now I have a TransactionManager type bean named "springCloudTaskTransactionManager" in the context which is responsible for managing the transactions of the TASK repository. Now I want to configure the same transaction manager for my JOB repository but here I didn't understand how the auto-configurations work. Specifically, I identified that for Spring Batch (unlike Spring Task
) there are two configurators (BatchConfigurer) implementation:
BasicBatchConfigurer, that came from org.springframework.boot:spring-boot-autoconfigure:2.7.18 (Spring Boot). Is the one that is actually picked-up in my application and is instantiated as a bean via theBatchAutoConfigurationauto-config condition@ConditionalOnMissingBean(BatchConfigurer.class). This configurator creates his ownTransactionManagerthat is different from the "springCloudTaskTransactionManager"DefaultBatchConfigurer, that came from org.springframework.batch:spring-batch-core:4.3.10 (Spring Batch). This is annotated as@Componentbut is ignored during the 1. auto-config (i don't understand why) and it is directly instantiated in the absence of a bean of typeBatchConfigurer.classby the@EnableBatchProcessingannotation. Also in this case he creates his ownTransactionManager.
In both cases it is through @EnableBatchProcessing -> SimpleBatchConfiguration which the TransactionManager is exposed in the context through this set-up:
@Bean
public PlatformTransactionManager transactionManager() throws Exception {
return createLazyProxy(transactionManager, PlatformTransactionManager.class);
}
where transactionManager it is respectively the one created by one of the two configurators and is qualified as a generic "transactionManager" (problems in sight with other datasource/transactions Spring Boot auto-configurations?)
So after all this magic we have two transactionManagers the one qualified as "springCloudTaskTransactionManager" and the one as "transactionManager".
Questions
I would like to ask for help in understanding some things:
- Why do we have two "basic" configurators and why is only the first one (the one from Spring Boot) seen by
@EnableBatchProcessing? That there is nothing wrong but why? - Could
@EnableBatchProcessinghave some unwanted behavior with Spring Boot, in particular regarding the transactionManager? - How do I configure the same
TransactionManagerfor the TASK repository and the JOB repository?
Introduction
Using Spring Cloud Task together with Spring Boot and Spring Batch is probably the most common use case for Spring Cloud Task. But it is not the only supported one.
For example, you can also use just Spring Batch (with or without Spring Boot) or you can use Spring Cloud Task with Spring Boot but without Spring Batch. Each of these possibilities comes with its own bootstrapping facilities, and there are multiple ways how to define the transaction management.
This can indeed be confusing and that's basically why a lot of this has been revamped for Spring Boot 3. But it's all doable with Spring Boot 2.
BatchConfigurer
The annotation
@ComponentonDefaultBatchConfigurerdoesn't do anything in your case. You should ignore it.@Componentmarks classes for auto-scanning but Spring Boot will only scan the packages below your main application class (unless you specify something else). By default, Spring Boot will not auto-scan any dependencies like Spring Batch for bean definitions.The class
SimpleBatchConfigurationis imported by using@EnableBatchProcessing. It's methodinitializedetermines theBatchConfigurerthat will be used: If the application context contains aBatchConfigurer, then it will be used. Otherwise, aDefaultBatchConfigurerwill be instantiated.The
BasicBatchConfigurerthat you see being used is added to the application context by Spring Boot'sBatchConfigurerConfiguration. This configuration class will expose one of twoBatchConfigurers depending on whether JPA is on the classpath or not. ButBatchConfigurerConfigurationis annotated with@ConditionalOnMissingBean(BatchConfigurer.class), which means that it will back off (and not do anything) if you expose your ownBatchConfigurer.Solution
Short answer
You need to implement and expose your own
BatchConfigurerandTaskConfigurer, which set theDataSourceandTransactionManageras you need.Long answer
You'll need your own implementation of
BatchConfigurerand expose it as a bean. It will then be used to set up Spring Batch because Spring Boot'sBatchConfigurerConfigurationwill back off.You can extend either
BasicBatchConfigurerorDefaultBatchConfigurer, set theDataSourcethat you want, and override the methodgetTransactionManagerto return the transaction manager that you want to use.With Spring Cloud Task 2.x, you can really only use a transaction manager that is called
springCloudTaskTransactionManageras it is referenced by name. You have two options:You can use
DefaultTaskConfigurerto set theDataSourcethat you want Spring Cloud to use, let it buildspringCloudTaskTransactionManager, and then inject this transaction manager into your customBatchConfigurer.Or you can implement your own
TaskConfigurer(e.g. by extendingDefaultTaskConfigurer) to return the transaction manager that you want ingetTransactionManager.