Remove datasource autowiring in AbstractBatchConfiguration
Before this commit, the datasource was autowired in AbstractBatchConfiguration. This was causing context startup failures when no datasource or more than one datasource is present in the context. This commit fixes these failures by looking for the datasource in the application context. This also prevents cyclic configuration dependencies when the datasource bean is defined in the same class where other batch artifacts are autowired. Resolves #3991
This commit is contained in:
@@ -25,7 +25,10 @@ import org.springframework.batch.core.explore.JobExplorer;
|
||||
import org.springframework.batch.core.launch.JobLauncher;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
@@ -50,7 +53,7 @@ import org.springframework.util.Assert;
|
||||
public abstract class AbstractBatchConfiguration implements ImportAware, InitializingBean {
|
||||
|
||||
@Autowired
|
||||
private DataSource dataSource;
|
||||
private ApplicationContext context;
|
||||
|
||||
private BatchConfigurer configurer;
|
||||
|
||||
@@ -153,7 +156,20 @@ public abstract class AbstractBatchConfiguration implements ImportAware, Initial
|
||||
return this.configurer;
|
||||
}
|
||||
if (configurers == null || configurers.isEmpty()) {
|
||||
DefaultBatchConfigurer configurer = new DefaultBatchConfigurer(this.dataSource);
|
||||
DataSource dataSource;
|
||||
try {
|
||||
dataSource = this.context.getBean(DataSource.class);
|
||||
} catch (NoUniqueBeanDefinitionException exception) {
|
||||
throw new IllegalStateException(
|
||||
"Multiple data sources are defined in the application context and no primary candidate was found. " +
|
||||
"To use the default BatchConfigurer, one of the data sources should be annotated with '@Primary'.",
|
||||
exception);
|
||||
} catch (NoSuchBeanDefinitionException exception) {
|
||||
throw new IllegalStateException(
|
||||
"To use the default BatchConfigurer, the application context must contain at least one data source.",
|
||||
exception);
|
||||
}
|
||||
DefaultBatchConfigurer configurer = new DefaultBatchConfigurer(dataSource);
|
||||
configurer.initialize();
|
||||
this.configurer = configurer;
|
||||
return configurer;
|
||||
|
||||
@@ -62,7 +62,7 @@ import org.springframework.transaction.PlatformTransactionManager;
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* The user should to provide a {@link DataSource} as a bean in the context, or else implement {@link BatchConfigurer} in
|
||||
* The user should provide a {@link DataSource} as a bean in the context, or else implement {@link BatchConfigurer} in
|
||||
* the configuration class itself, e.g.
|
||||
*
|
||||
* <pre class="code">
|
||||
@@ -85,11 +85,8 @@ import org.springframework.transaction.PlatformTransactionManager;
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* If multiple {@link javax.sql.DataSource}s are defined in the context, the one annotated with
|
||||
* {@link org.springframework.context.annotation.Primary} will be used (Note that if none
|
||||
* of them is annotated with {@link org.springframework.context.annotation.Primary}, the one
|
||||
* named <code>dataSource</code> will be used if any, otherwise a {@link UnsatisfiedDependencyException}
|
||||
* will be thrown).
|
||||
* If multiple {@link javax.sql.DataSource}s are defined in the context, the primary autowire candidate
|
||||
* will be used, otherwise an exception will be thrown.
|
||||
*
|
||||
* Note that only one of your configuration classes needs to have the <code>@EnableBatchProcessing</code>
|
||||
* annotation. Once you have an <code>@EnableBatchProcessing</code> class in your configuration you will have an
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
* Copyright 2012-2022 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.
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2022 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.batch.core.configuration.annotation;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.batch.core.ExitStatus;
|
||||
import org.springframework.batch.core.Job;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.JobParameters;
|
||||
import org.springframework.batch.core.JobParametersInvalidException;
|
||||
import org.springframework.batch.core.launch.JobLauncher;
|
||||
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
|
||||
import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
|
||||
import org.springframework.batch.core.repository.JobRestartException;
|
||||
import org.springframework.batch.repeat.RepeatStatus;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration
|
||||
public class InlineDataSourceDefinitionTests {
|
||||
|
||||
@Test
|
||||
public void testInlineDataSourceDefinition() throws Exception {
|
||||
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyJobConfiguration.class);
|
||||
Job job = applicationContext.getBean(Job.class);
|
||||
JobLauncher jobLauncher = applicationContext.getBean(JobLauncher.class);
|
||||
JobExecution jobExecution = jobLauncher.run(job, new JobParameters());
|
||||
Assert.assertEquals(ExitStatus.COMPLETED, jobExecution.getExitStatus());
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableBatchProcessing
|
||||
static class MyJobConfiguration {
|
||||
|
||||
private JobBuilderFactory jobs;
|
||||
private StepBuilderFactory steps;
|
||||
|
||||
public MyJobConfiguration(JobBuilderFactory jobs, StepBuilderFactory steps) {
|
||||
this.jobs = jobs;
|
||||
this.steps = steps;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Job job() {
|
||||
return jobs.get("job")
|
||||
.start(steps.get("step")
|
||||
.tasklet((contribution, chunkContext) -> {
|
||||
System.out.println("hello world");
|
||||
return RepeatStatus.FINISHED;
|
||||
})
|
||||
.build())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DataSource dataSource() {
|
||||
return new EmbeddedDatabaseBuilder()
|
||||
.setType(EmbeddedDatabaseType.H2)
|
||||
.addScript("/org/springframework/batch/core/schema-drop-h2.sql")
|
||||
.addScript("/org/springframework/batch/core/schema-h2.sql")
|
||||
.generateUniqueName(true)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2018-2021 the original author or authors.
|
||||
* Copyright 2018-2022 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.
|
||||
@@ -21,11 +21,14 @@ import javax.sql.DataSource;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.batch.core.Job;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.batch.repeat.RepeatStatus;
|
||||
import org.springframework.beans.factory.UnsatisfiedDependencyException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
|
||||
import org.springframework.test.util.AopTestUtils;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
@@ -35,11 +38,6 @@ import org.springframework.transaction.PlatformTransactionManager;
|
||||
*/
|
||||
public class TransactionManagerConfigurationWithBatchConfigurerTests extends TransactionManagerConfigurationTests {
|
||||
|
||||
@Test(expected = UnsatisfiedDependencyException.class)
|
||||
public void testConfigurationWithNoDataSourceAndNoTransactionManager() {
|
||||
new AnnotationConfigApplicationContext(BatchConfigurationWithNoDataSourceAndNoTransactionManager.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConfigurationWithDataSourceAndNoTransactionManager() throws Exception {
|
||||
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(BatchConfigurationWithDataSourceAndNoTransactionManager.class);
|
||||
@@ -62,11 +60,7 @@ public class TransactionManagerConfigurationWithBatchConfigurerTests extends Tra
|
||||
Assert.assertSame(getTransactionManagerSetOnJobRepository(applicationContext.getBean(JobRepository.class)), transactionManager);
|
||||
}
|
||||
|
||||
@EnableBatchProcessing
|
||||
public static class BatchConfigurationWithNoDataSourceAndNoTransactionManager {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableBatchProcessing
|
||||
public static class BatchConfigurationWithDataSourceAndNoTransactionManager {
|
||||
@Bean
|
||||
@@ -80,6 +74,7 @@ public class TransactionManagerConfigurationWithBatchConfigurerTests extends Tra
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableBatchProcessing
|
||||
public static class BatchConfigurationWithDataSourceAndTransactionManager {
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2018-2021 the original author or authors.
|
||||
* Copyright 2018-2022 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.
|
||||
@@ -21,11 +21,15 @@ import javax.sql.DataSource;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.batch.core.JobParameters;
|
||||
import org.springframework.batch.core.launch.JobLauncher;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.UnsatisfiedDependencyException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
|
||||
import org.springframework.test.util.AopTestUtils;
|
||||
@@ -36,14 +40,22 @@ import org.springframework.transaction.PlatformTransactionManager;
|
||||
*/
|
||||
public class TransactionManagerConfigurationWithoutBatchConfigurerTests extends TransactionManagerConfigurationTests {
|
||||
|
||||
@Test(expected = UnsatisfiedDependencyException.class)
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void testConfigurationWithNoDataSourceAndNoTransactionManager() {
|
||||
new AnnotationConfigApplicationContext(BatchConfigurationWithNoDataSourceAndNoTransactionManager.class);
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BatchConfigurationWithNoDataSourceAndNoTransactionManager.class);
|
||||
// beans created by `@EnableBatchProcessing` are lazy proxies, SimpleBatchConfiguration.initialize is only triggered
|
||||
// when a method is called on one of these proxies
|
||||
JobRepository jobRepository = context.getBean(JobRepository.class);
|
||||
Assert.assertFalse(jobRepository.isJobInstanceExists("myJob", new JobParameters()));
|
||||
}
|
||||
|
||||
@Test(expected = UnsatisfiedDependencyException.class)
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void testConfigurationWithNoDataSourceAndTransactionManager() {
|
||||
new AnnotationConfigApplicationContext(BatchConfigurationWithNoDataSourceAndTransactionManager.class);
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BatchConfigurationWithNoDataSourceAndTransactionManager.class);
|
||||
// beans created by `@EnableBatchProcessing` are lazy proxies, SimpleBatchConfiguration.initialize is only triggered
|
||||
// when a method is called on one of these proxies
|
||||
JobRepository jobRepository = context.getBean(JobRepository.class);
|
||||
Assert.assertFalse(jobRepository.isJobInstanceExists("myJob", new JobParameters()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -73,14 +85,15 @@ public class TransactionManagerConfigurationWithoutBatchConfigurerTests extends
|
||||
// In this case, the supplied primary transaction manager won't be used by batch and a DataSourceTransactionManager will be used instead.
|
||||
// The user has to provide a custom BatchConfigurer.
|
||||
Assert.assertTrue(getTransactionManagerSetOnJobRepository(applicationContext.getBean(JobRepository.class)) instanceof DataSourceTransactionManager);
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableBatchProcessing
|
||||
public static class BatchConfigurationWithNoDataSourceAndNoTransactionManager {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableBatchProcessing
|
||||
public static class BatchConfigurationWithNoDataSourceAndTransactionManager {
|
||||
|
||||
@@ -90,6 +103,7 @@ public class TransactionManagerConfigurationWithoutBatchConfigurerTests extends
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableBatchProcessing
|
||||
public static class BatchConfigurationWithDataSourceAndNoTransactionManager {
|
||||
|
||||
@@ -99,6 +113,7 @@ public class TransactionManagerConfigurationWithoutBatchConfigurerTests extends
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableBatchProcessing
|
||||
public static class BatchConfigurationWithDataSourceAndOneTransactionManager {
|
||||
|
||||
@@ -113,6 +128,7 @@ public class TransactionManagerConfigurationWithoutBatchConfigurerTests extends
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableBatchProcessing
|
||||
public static class BatchConfigurationWithDataSourceAndMultipleTransactionManagers {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user