java - spring batch dependency cycle - Stack Overflow

admin2025-04-18  3

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.

Share Improve this question asked Jan 29 at 15:52 kimo815kimo815 359 bronze badges 1
  • 1 Ditch the @Autowired fields and just add method arguments to the bankJob method to get the needed reader, writer and processor. – M. Deinum Commented Jan 30 at 6:45
Add a comment  | 

2 Answers 2

Reset to default 1

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

转载请注明原文地址:http://anycun.com/QandA/1744954750a89980.html