diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/AbstractBatchConfiguration.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/AbstractBatchConfiguration.java index 14ef6de1b..7c6918789 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/AbstractBatchConfiguration.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/AbstractBatchConfiguration.java @@ -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; diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/EnableBatchProcessing.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/EnableBatchProcessing.java index 33ab4284e..d89eaf5c7 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/EnableBatchProcessing.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/EnableBatchProcessing.java @@ -62,7 +62,7 @@ import org.springframework.transaction.PlatformTransactionManager; * } * * - * 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. * *
@@ -85,11 +85,8 @@ import org.springframework.transaction.PlatformTransactionManager;
  * }
  * 
* - * 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 dataSource 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 @EnableBatchProcessing * annotation. Once you have an @EnableBatchProcessing class in your configuration you will have an diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/SimpleBatchConfiguration.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/SimpleBatchConfiguration.java index f6f9a36f8..caacabec7 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/SimpleBatchConfiguration.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/SimpleBatchConfiguration.java @@ -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. diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/annotation/InlineDataSourceDefinitionTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/annotation/InlineDataSourceDefinitionTests.java new file mode 100644 index 000000000..d9a5d07b8 --- /dev/null +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/annotation/InlineDataSourceDefinitionTests.java @@ -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(); + } + } +} diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/annotation/TransactionManagerConfigurationWithBatchConfigurerTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/annotation/TransactionManagerConfigurationWithBatchConfigurerTests.java index a202bd531..445dae97d 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/annotation/TransactionManagerConfigurationWithBatchConfigurerTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/annotation/TransactionManagerConfigurationWithBatchConfigurerTests.java @@ -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 { diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/annotation/TransactionManagerConfigurationWithoutBatchConfigurerTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/annotation/TransactionManagerConfigurationWithoutBatchConfigurerTests.java index 23fc07587..c384a2e5e 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/annotation/TransactionManagerConfigurationWithoutBatchConfigurerTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/annotation/TransactionManagerConfigurationWithoutBatchConfigurerTests.java @@ -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 {