我的 Spring Boot 应用程序调用示例 Spring Batch 作业的配置如下:
MainApplication.java:
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(MainApplication.class).web(WebApplicationType.NONE).run(args);
}
}
AggregateItemJobConfiguration.java:
@Slf4j
@Configuration
@EnableBatchProcessing
public class AggregateItemJobConfiguration {
@Autowired
private JobBuilderFactory jobBuilderFactory;
@Autowired
private StepBuilderFactory stepBuilderFactory;
/*
@Bean
public EmbeddedDatabase dataSource() {
return new EmbeddedDatabaseBuilder().build();
}
@Bean
public JobRepositoryFactoryBean myJobRepository(DataSource dataSource) {
JobRepositoryFactoryBean jobRepositoryFactoryBean = new JobRepositoryFactoryBean();
jobRepositoryFactoryBean.setDataSource(dataSource);
return jobRepositoryFactoryBean;
}
*/
@Bean
public Job job() throws Exception {
return this.jobBuilderFactory
.get("multilineJob")
.incrementer(new RunIdIncrementer())
.flow(step())
.end()
.build();
}
@Bean
public Step step() throws Exception {
return this.stepBuilderFactory.get("step1")
.<Trade, Trade>chunk(1)
.reader(reader())
.writer(writer())
.processor(processor())
.build();
}
@Bean
public ItemProcessor<Trade, Trade> processor() {
return new ItemProcessor<Trade, Trade>() {
@Override
public Trade process(Trade item) throws Exception {
log.info("In processor: "+item.getCustomer());
item.setProcessed(true);
return item;
}
};
}
@Bean
@StepScope
public FlatFileItemWriter<Trade> writer(
) {
return new FlatFileItemWriterBuilder<Trade>()
.name("tradeWriter")
.resource(new FileSystemResource("target/test-outputs/20070122.testStream.multilineStep.txt"))
.lineAggregator(new PassThroughLineAggregator<Trade>())
.build();
}
@Bean
public ItemReader<Trade> reader() {
AggregateItemReader<Trade> aggregateItemReader = new AggregateItemReader<>();
aggregateItemReader.setItemReader(fileItemReader());
return new DequeueItemReader<>(aggregateItemReader);
}
@Bean
@StepScope
public FlatFileItemReader<AggregateItem<Trade>> fileItemReader(
) {
return new FlatFileItemReaderBuilder<AggregateItem<Trade>>()
.name("tradeFileItemReader")
.resource(new ClassPathResource("data/20070122.teststream.multilineStep.txt"))
.linesToSkip(0)
.lineTokenizer(fixedFileDescriptor())
.fieldSetMapper(fieldSetMapper())
.build();
}
@Bean
public AggregateItemFieldSetMapper<Trade> fieldSetMapper() {
AggregateItemFieldSetMapper<Trade> fieldSetMapper = new AggregateItemFieldSetMapper<>();
fieldSetMapper.setDelegate(new TradeFieldSetMapper());
return fieldSetMapper;
}
@SuppressWarnings("serial")
@Bean
public PatternMatchingCompositeLineTokenizer fixedFileDescriptor() {
PatternMatchingCompositeLineTokenizer lineTokenizer = new PatternMatchingCompositeLineTokenizer();
Map<String, LineTokenizer> tokenizers = new HashMap<String, LineTokenizer>() {
{
put("BEGIN*", beginRecordTokenizer());
put("END*", endRecordTokenizer());
put("*", tradeRecordTokenizer());
}
};
lineTokenizer.setTokenizers(tokenizers);
return lineTokenizer;
}
@Bean
public FixedLengthTokenizer beginRecordTokenizer() {
FixedLengthTokenizer tokenizer = new FixedLengthTokenizer();
tokenizer.setColumns(new Range[] { new Range(1, 5) });
return tokenizer;
}
@Bean
public FixedLengthTokenizer endRecordTokenizer() {
FixedLengthTokenizer tokenizer = new FixedLengthTokenizer();
tokenizer.setColumns(new Range[] { new Range(1, 3) });
return tokenizer;
}
@Bean
public FixedLengthTokenizer tradeRecordTokenizer() {
FixedLengthTokenizer tokenizer = new FixedLengthTokenizer();
tokenizer.setNames(new String[] { "ISIN", "Quantity", "Price", "Customer" });
tokenizer.setColumns(new Range[] {
new Range(1, 12),
new Range(13, 15),
new Range(16, 20),
new Range(21, 29) });
return tokenizer;
}
}
当在IDE中作为Spring Boot应用程序运行时,运行成功(在我在pom.xml中添加了h2依赖之后:
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
没有它(由于某种原因)它就无法工作:
***************************
APPLICATION FAILED TO START
***************************
Description:
Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.
Reason: Failed to determine a suitable driver class
Action:
Consider the following:
If you want an embedded database (H2, HSQL or Derby), please put it on the classpath.
If you have database settings to be loaded from a particular profile you may need to activate it (no profiles are currently active).
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
[32m :: Spring Boot :: [39m [2m (v2.7.14)[0;39m
[2m2023-08-22 15:55:59.664[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ main][0;39m [36mc.p.g.f.d.d.a.provider.MainApplication [0;39m [2m:[0;39m Starting MainApplication using Java 11.0.16.1 on VXABZS4H9100738 with PID 18040 (C:\code\STS\return-data-processor\target\classes started by x266345 in C:\code\STS\return-data-processor)
[2m2023-08-22 15:55:59.668[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ main][0;39m [36mc.p.g.f.d.d.a.provider.MainApplication [0;39m [2m:[0;39m No active profile set, falling back to 1 default profile: "default"
[2m2023-08-22 15:56:00.641[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ main][0;39m [36mfaultConfiguringBeanFactoryPostProcessor[0;39m [2m:[0;39m No bean named 'errorChannel' has been explicitly defined. Therefore, a default PublishSubscribeChannel will be created.
[2m2023-08-22 15:56:00.664[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ main][0;39m [36mfaultConfiguringBeanFactoryPostProcessor[0;39m [2m:[0;39m No bean named 'integrationHeaderChannelRegistry' has been explicitly defined. Therefore, a default DefaultHeaderChannelRegistry will be created.
[2m2023-08-22 15:56:01.201[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ main][0;39m [36mcom.zaxxer.hikari.HikariDataSource [0;39m [2m:[0;39m HikariPool-1 - Starting...
[2m2023-08-22 15:56:01.617[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ main][0;39m [36mcom.zaxxer.hikari.HikariDataSource [0;39m [2m:[0;39m HikariPool-1 - Start completed.
[2m2023-08-22 15:56:01.949[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ main][0;39m [36mo.s.b.c.r.s.JobRepositoryFactoryBean [0;39m [2m:[0;39m No database type set, using meta data indicating: H2
[2m2023-08-22 15:56:01.976[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ main][0;39m [36mo.s.b.c.l.support.SimpleJobLauncher [0;39m [2m:[0;39m No TaskExecutor has been set, defaulting to synchronous executor.
[2m2023-08-22 15:56:02.310[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ main][0;39m [36mo.s.i.endpoint.EventDrivenConsumer [0;39m [2m:[0;39m Adding {logging-channel-adapter:_org.springframework.integration.errorLogger} as a subscriber to the 'errorChannel' channel
[2m2023-08-22 15:56:02.310[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ main][0;39m [36mo.s.i.channel.PublishSubscribeChannel [0;39m [2m:[0;39m Channel 'application.errorChannel' has 1 subscriber(s).
[2m2023-08-22 15:56:02.311[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ main][0;39m [36mo.s.i.endpoint.EventDrivenConsumer [0;39m [2m:[0;39m started bean '_org.springframework.integration.errorLogger'
[2m2023-08-22 15:56:02.322[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ main][0;39m [36mc.p.g.f.d.d.a.provider.MainApplication [0;39m [2m:[0;39m Started MainApplication in 3.163 seconds (JVM running for 8.508)
[2m2023-08-22 15:56:02.324[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ main][0;39m [36mo.s.b.a.b.JobLauncherApplicationRunner [0;39m [2m:[0;39m Running default command line with: []
[2m2023-08-22 15:56:02.401[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ main][0;39m [36mo.s.b.c.l.support.SimpleJobLauncher [0;39m [2m:[0;39m Job: [FlowJob: [name=multilineJob]] launched with the following parameters: [{run.id=1}]
[2m2023-08-22 15:56:02.447[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ main][0;39m [36mo.s.batch.core.job.SimpleStepHandler [0;39m [2m:[0;39m Executing step: [step1]
[2m2023-08-22 15:56:02.509[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ main][0;39m [36mc.p.g.f.d.d.a.p.DequeueItemReader [0;39m [2m:[0;39m Is Queue empty: true
[2m2023-08-22 15:56:02.524[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ main][0;39m [36mc.p.g.f.d.d.a.p.DequeueItemReader [0;39m [2m:[0;39m Aggregated read size: 2
[2m2023-08-22 15:56:02.524[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ main][0;39m [36mc.p.g.f.d.d.a.p.DequeueItemReader [0;39m [2m:[0;39m Queue size: 2
[2m2023-08-22 15:56:02.528[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ main][0;39m [36m.f.d.d.a.p.AggregateItemJobConfiguration[0;39m [2m:[0;39m In processor: customer1
[2m2023-08-22 15:56:02.533[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ main][0;39m [36mc.p.g.f.d.d.a.p.DequeueItemReader [0;39m [2m:[0;39m Is Queue empty: false
[2m2023-08-22 15:56:02.533[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ main][0;39m [36mc.p.g.f.d.d.a.p.DequeueItemReader [0;39m [2m:[0;39m Queue size: 1
[2m2023-08-22 15:56:02.533[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ main][0;39m [36m.f.d.d.a.p.AggregateItemJobConfiguration[0;39m [2m:[0;39m In processor: customer2
[2m2023-08-22 15:56:02.535[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ main][0;39m [36mc.p.g.f.d.d.a.p.DequeueItemReader [0;39m [2m:[0;39m Is Queue empty: true
[2m2023-08-22 15:56:02.536[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ main][0;39m [36mc.p.g.f.d.d.a.p.DequeueItemReader [0;39m [2m:[0;39m Aggregated read size: 3
[2m2023-08-22 15:56:02.536[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ main][0;39m [36mc.p.g.f.d.d.a.p.DequeueItemReader [0;39m [2m:[0;39m Queue size: 3
[2m2023-08-22 15:56:02.536[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ main][0;39m [36m.f.d.d.a.p.AggregateItemJobConfiguration[0;39m [2m:[0;39m In processor: customer2
[2m2023-08-22 15:56:02.538[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ main][0;39m [36mc.p.g.f.d.d.a.p.DequeueItemReader [0;39m [2m:[0;39m Is Queue empty: false
[2m2023-08-22 15:56:02.538[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ main][0;39m [36mc.p.g.f.d.d.a.p.DequeueItemReader [0;39m [2m:[0;39m Queue size: 2
[2m2023-08-22 15:56:02.538[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ main][0;39m [36m.f.d.d.a.p.AggregateItemJobConfiguration[0;39m [2m:[0;39m In processor: customer3
[2m2023-08-22 15:56:02.540[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ main][0;39m [36mc.p.g.f.d.d.a.p.DequeueItemReader [0;39m [2m:[0;39m Is Queue empty: false
[2m2023-08-22 15:56:02.540[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ main][0;39m [36mc.p.g.f.d.d.a.p.DequeueItemReader [0;39m [2m:[0;39m Queue size: 1
[2m2023-08-22 15:56:02.540[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ main][0;39m [36m.f.d.d.a.p.AggregateItemJobConfiguration[0;39m [2m:[0;39m In processor: customer4
[2m2023-08-22 15:56:02.543[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ main][0;39m [36mc.p.g.f.d.d.a.p.DequeueItemReader [0;39m [2m:[0;39m Is Queue empty: true
[2m2023-08-22 15:56:02.543[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ main][0;39m [36mc.p.g.f.d.d.a.p.DequeueItemReader [0;39m [2m:[0;39m Queue size: 0
[2m2023-08-22 15:56:02.545[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ main][0;39m [36mo.s.batch.core.step.AbstractStep [0;39m [2m:[0;39m Step: [step1] executed in 98ms
[2m2023-08-22 15:56:02.552[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ main][0;39m [36mo.s.b.c.l.support.SimpleJobLauncher [0;39m [2m:[0;39m Job: [FlowJob: [name=multilineJob]] completed with the following parameters: [{run.id=1}] and the following status: [COMPLETED] in 124ms
[2m2023-08-22 15:56:02.559[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ionShutdownHook][0;39m [36mo.s.i.endpoint.EventDrivenConsumer [0;39m [2m:[0;39m Removing {logging-channel-adapter:_org.springframework.integration.errorLogger} as a subscriber to the 'errorChannel' channel
[2m2023-08-22 15:56:02.561[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ionShutdownHook][0;39m [36mo.s.i.channel.PublishSubscribeChannel [0;39m [2m:[0;39m Channel 'application.errorChannel' has 0 subscriber(s).
[2m2023-08-22 15:56:02.561[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ionShutdownHook][0;39m [36mo.s.i.endpoint.EventDrivenConsumer [0;39m [2m:[0;39m stopped bean '_org.springframework.integration.errorLogger'
[2m2023-08-22 15:56:02.583[0;39m [33m WARN[0;39m [35m18040[0;39m [2m---[0;39m [2m[ionShutdownHook][0;39m [36mo.s.b.f.support.DisposableBeanAdapter [0;39m [2m:[0;39m Custom destroy method 'close' on bean with name 'reader' threw an exception: org.springframework.beans.factory.support.ScopeNotActiveException: Error creating bean with name 'scopedTarget.fileItemReader': Scope 'step' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No context holder available for step scope
[2m2023-08-22 15:56:02.583[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ionShutdownHook][0;39m [36mcom.zaxxer.hikari.HikariDataSource [0;39m [2m:[0;39m HikariPool-1 - Shutdown initiated...
[2m2023-08-22 15:56:02.591[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ionShutdownHook][0;39m [36mcom.zaxxer.hikari.HikariDataSource [0;39m [2m:[0;39m HikariPool-1 - Shutdown completed.
我想重构该批处理应用程序由相应的基于JUnit 5的测试调用(将对其他批处理应用程序进行单独的测试)。像这样的东西:
AggregateItemJobTest.java
@Slf4j
@SpringBatchTest
@SpringJUnitConfig(AggregateItemJobConfiguration.class)
class AggregateItemJobTest {
@Autowired
private JobLauncherTestUtils jobLauncherTestUtils;
@Autowired
private JobRepositoryTestUtils jobRepositoryTestUtils;
private JdbcTemplate jdbcTemplate;
@Autowired
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
@AfterEach
public void cleanUp() {
jobRepositoryTestUtils.removeJobExecutions();
}
@Autowired
public JobLauncher jobLauncher ;
@Test
void testJob() throws Exception {
log.info("In test");
JobExecution jobExecution = jobLauncherTestUtils.launchJob();
}
}
运行会产生以下结果:
org.springframework.beans.factory.support.ScopeNotActiveException: Error creating bean with name 'scopedTarget.fileItemReader': Scope 'step' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No context holder available for step scope
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:383)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
..........
at
at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:60)
at org.eclipse.jdt.internal.junit5.runner.JUnit5TestReference.run(JUnit5TestReference.java:98)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:40)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:529)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:756)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:452)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:210)
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'jobRepositoryTestUtils': Unsatisfied dependency expressed through method 'setDataSource' parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'javax.sql.DataSource' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:774)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:727)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1431)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:619)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:955)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:921)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583)
at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:127)
at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:60)
at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.delegateLoading(AbstractDelegatingSmartContextLoader.java:276)
at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.loadContext(AbstractDelegatingSmartContextLoader.java:244)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:141)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:90)
... 71 common frames omitted
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'javax.sql.DataSource' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1801)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1357)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1311)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:766)
... 90 common frames omitted
16:05:35.641 [main] DEBUG org.springframework.test.context.support.AbstractDirtiesContextTestExecutionListener - After test class: context [DefaultTestContext@409c54f testClass = AggregateItemJobTest, testInstance = [null], testMethod = [null], testException = [null], mergedContextConfiguration = [MergedContextConfiguration@3e74829 testClass = AggregateItemJobTest, locations = '{}', classes = '{class com.pru.globalpayments.feeds.downstream.dailycashreport.acquire.provider.AggregateItemJobConfiguration}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextCustomizers = set[org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@44be0077, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@51a9ad5e, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.autoconfigure.actuate.metrics.MetricsExportContextCustomizerFactory$DisableMetricExportContextCustomizer@42b3b079, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@24c4ddae, org.springframework.batch.test.context.BatchTestContextCustomizer@4a3631f8], contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]], attributes = map['org.springframework.test.context.event.ApplicationEventsTestExecutionListener.recordApplicationEvents' -> false]], class annotated with @DirtiesContext [false] with mode [null].
尝试根据此处的建议单独添加数据源,会中断之前运行的MainApplication.java
执行。请在评论中查看更多说明以及相关的堆栈跟踪。有人可以给我指出一个从 JUnit 5 测试运行 Spring Batch 作业的完整工作示例(最小但完整)。
TIA。
具有上述代码库的存储库可以在这里找到:
有人可以给我指出一个从 JUnit 5 测试运行 Spring Batch 作业的完整工作示例(最小但完整)。这是一个基于 start.spring.io 生成的项目的最小完整示例,具有 Spring Batch 和 H2 依赖项:
https://start.spring.io/#!type=maven-project&language=java&platformVersion=2.7.15&packaging=jar&jvmVersion=1.8&groupId=com.example&artifactId=demo&name=demo&description=Demo%20project%20for%20Spring%20Boot&packageName=com .example.demo&dependencies=batch,h2
src/main/java/com/example/demo/DemoApplication.java
中的主类:
package com.example.demo;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@EnableBatchProcessing
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Bean
public Job job(JobBuilderFactory jobs, StepBuilderFactory steps) {
return jobs.get("job")
.start(steps.get("step")
.tasklet((contribution, chunkContext) -> {
System.out.println("hello world");
return RepeatStatus.FINISHED;
})
.build())
.build();
}
}
src/test/java/com/example/demo/DemoApplicationTests.java
中的测试:
package com.example.demo;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.test.JobLauncherTestUtils;
import org.springframework.batch.test.context.SpringBatchTest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBatchTest
@SpringBootTest
class DemoApplicationTests {
@Autowired
private JobLauncherTestUtils jobLauncherTestUtils;
@Test
void contextLoads() throws Exception {
//when
JobExecution jobExecution = jobLauncherTestUtils.launchJob();
// then
Assertions.assertEquals(ExitStatus.COMPLETED, jobExecution.getExitStatus());
}
}
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.15</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.batch</groupId>
<artifactId>spring-batch-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.properties
文件为空。完成后,主应用程序以及测试都顺利通过。