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.
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 problem is that Spring needs to instantiate SpringBatchConfig
and inject all its fields before it can call the method flatFileItemReader
on the instantiated SpringBatchConfig
.
So you are asking Spring to inject FlatFileItemReader
into SpringBatchConfig
before it can create the FlatFileItemReader
.
You can fix this, as you mentioned, by building the FlatFileItemReader
in a separate configuration class that does not ask for the reader to be injected.
Alternatively, you can make the methods flatFileItemReader
and lineMapper
static
. When the two methods are static
, they do not require an object of type SpringBatchConfig
to be instantiated, which breaks the cycle.
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
@Autowired
fields and just add method arguments to thebankJob
method to get the needed reader, writer and processor. – M. Deinum Commented Jan 30 at 6:45