i'm testing spring batch and i'm getting dependency cycle. I couldn't understand what is the issue here.
package org.id.demo.test.dao;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.core.step.tasklet.TaskletStep;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.file.FlatFileItemReader;
import org.springframework.batch.item.file.LineMapper;
import org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper;
import org.springframework.batch.item.file.mapping.DefaultLineMapper;
import org.springframework.batch.item.file.transform.DelimitedLineTokenizer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.transaction.PlatformTransactionManager;
@Configuration
@EnableBatchProcessing
public class SpringBatchConfig {
@Autowired
private JobRepository jobRepository;
@Autowired
private PlatformTransactionManager platformTransactionManager;
@Autowired
private ItemWriter<BankTransaction> bankTransactionItemWriter;
@Autowired
private ItemProcessor<BankTransaction, BankTransaction> bankTransactionItemProcessor;
@Autowired
private ItemReader<BankTransaction> flatFileItemReader;
@Bean
public Job bankJob() {
TaskletStep step1 = new StepBuilder("step-load-data", jobRepository)
.<BankTransaction, BankTransaction>chunk(100, platformTransactionManager)
.reader(flatFileItemReader)
.processor(bankTransactionItemProcessor)
.writer(bankTransactionItemWriter)
.build();
return new JobBuilder("ETL-job", jobRepository)
.start(step1)
.build();
}
@Bean
public FlatFileItemReader<BankTransaction> flatFileItemReader(@Value("${inputFile}") Resource inputFile) {
FlatFileItemReader<BankTransaction> fileItemReader = new FlatFileItemReader<BankTransaction>();
fileItemReader.setName("just a reader name");
fileItemReader.setLinesToSkip(1);
fileItemReader.setResource(inputFile);
fileItemReader.setLineMapper(lineMapper());
return fileItemReader;
}
@Bean
public LineMapper<BankTransaction> lineMapper() {
DefaultLineMapper<BankTransaction> lineMapper = new DefaultLineMapper<>();
DelimitedLineTokenizer lineTokenizer = new DelimitedLineTokenizer();
lineTokenizer.setDelimiter(DelimitedLineTokenizer.DELIMITER_COMMA);
lineTokenizer.setStrict(false);
lineTokenizer.setNames("id", "accountID", "strTransactionDate", "transactionType", "amount");
lineMapper.setLineTokenizer(lineTokenizer);
BeanWrapperFieldSetMapper<BankTransaction> fieldSetMapper = new BeanWrapperFieldSetMapper<>();
fieldSetMapper.setTargetType(BankTransaction.class);
lineMapper.setFieldSetMapper(fieldSetMapper);
return lineMapper;
}
}
I'm always getting this error message:
The dependencies of some of the beans in the application context form a cycle:
jobRestController (field private org.springframework.batch.core.Job org.id.demo.test.dao.JobRestController.job)
┌─────┐
| springBatchConfig (field private org.springframework.batch.item.ItemReader org.id.demo.test.dao.SpringBatchConfig.flatFileItemReader)
└─────┘
Note: if i move the ItemReader to another class everything works fine.
I want to undertand what is the issue and how to fix it without put reader in another class.
The reason behind circular dependencies:
Spring tries to create SpringBatchConfig first because it's marked as @Configuration.
But SpringBatchConfig has @Autowired private ItemReader flatFileItemReader;
Spring now looks for a @Bean that provides flatFileItemReader and finds it inside the same class (SpringBatchConfig).
But the problem is SpringBatchConfig isn't fully initialized yet, so Spring can't create the flatFileItemReader bean.
This creates a circular dependency—Spring can't finish SpringBatchConfig without flatFileItemReader, but flatFileItemReader is inside SpringBatchConfig.
There are couple of ways to fix this:
Option 1: Instead of using @Autowired on ItemReader flatFileItemReader, let spring provides it when calling bankJob():
@Bean
public Job bankJob(FlatFileItemReader<BankTransaction> flatFileItemReader) { // Inject here
TaskletStep step1 = new StepBuilder("step-load-data", jobRepository)
.<BankTransaction, BankTransaction>chunk(100, platformTransactionManager)
.reader(flatFileItemReader)
.processor(bankTransactionItemProcessor)
.writer(bankTransactionItemWriter)
.build();
return new JobBuilder("ETL-job", jobRepository)
.start(step1)
.build();
}
When you pass flatFileItemReader as a method parameter in bankJob(), Spring creates it first before injecting it.
This guarantees that the reader exists before it is used in the bankJob.
If you still want to use @Autowired on ItemReader flatFileItemReader then
Option 2: Use @Lazy for delay Injection
@Autowired
@Lazy
private ItemReader<BankTransaction> flatFileItemReader;
This tells Spring to delay initializing flatFileItemReader until it's actually needed