Search code examples
javaspringspring-batch

NoUniqueBeanDefinitionException :expected single matching bean but found 2


I am migrating from spring-batch 3 (JDK11, j2ee) to version 5.1.1 (JDK 17, Jakarta).

I have a job with 2 steps. Each step is defined in a method that had the @Bean annotation. Those steps are then passed to the method that defined the actual Job, that is also @Bean-annotated, passing steps as method arguments.

Here is what my code looks like:

@ComponentScan({ "this.class.package" })
public class TestJob
    extends
    CommonBatchConfig
{
    @Autowired
    JobRepository jobRepository;

    @Bean("test")
    public Job testJob(Step step1, Step step2)
    {
        return new JobBuilder("test", jobRepository).start(step1).next(step2).build();
    }

    @Bean
    public Step step1(ItemReader reader, JpaTransactionManager transactionManager,
        ItamWriter writer, Listener listener)
    {
        return new StepBuilder("step1", jobRepository)
            .chunk(50, transactionManager)
            .reader(reader)
            .writer(writer).transactionManager(transactionManager).listener(listener)
            .build();
    }

    @Bean
    public Step step2(Tasklet tasklet, JpaTransactionManager transactionManager,
        Listener listener)
    {
        return new StepBuilder("step2", jobRepository).tasklet(tasklet, transactionManager)
            .listener(listener).build();
    }

}

The error I get during execution is: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'org.springframework.batch.core.Step' available: expected single matching bean but found 2: step1,step2

A workaround is to add a @Qualifier annotation to the arguments in the testJob method. Here is the fixed method:

    @Bean("test")
    public Job testJob(@Qualifier("step1") Step step1, @Qualifier("step2") Step step2)
    {
        return new JobBuilder("test", jobRepository).start(step1).next(step2).build();
    }

However, I don't like that solution, as it forces me to repeat the parameter name twice. It seems to me that a solution would be to perform bean resolution by name and not by type only, as it seems to be the case here.

Another solution seems to autowire ("@Autowired") steps as class fields. However, this solution seems worse than the previous one because I have otherwise zero incentive to add steps as class fields, because they are used only once in my class.

Can you please let me know how to fix my batch in a more proper way?


Solution

  • Alternative solution:

    @Bean("test")
    public Job testJob()
    {
         return new JobBuilder("test", jobRepository).start(step1()).next(step2()).build();
    }