Import job configurations into common job configuration class (Annotation config)

Viewed 200

I am refactoring a legacy Spring Batch XML based application to use annotation configuration instead. I want to understand how I can convert the following XML files into annotation based configuration and maintain the same separation of concern.

Here is a simple example for the sake of discussion.

job-config-1.xml

<job id="job1" xmlns="http://www.springframework.org/schema/batch">
               <batch:step id="step1">
                    <batch:tasklet throttle-limit="20" task-executor="taskExecutor">
                        <batch:chunk reader="reader1"
                            writer="writer1" commit-interval="500" />
                    </batch:tasklet>
                </batch:step>
</job>

job-config-2.xml

<job id="job2" xmlns="http://www.springframework.org/schema/batch">
                   <batch:step id="step2">
                        <batch:tasklet throttle-limit="20" task-executor="taskExecutor">
                            <batch:chunk reader="reader2"
                                writer="writer2" commit-interval="500" />
                        </batch:tasklet>
                    </batch:step>
</job>

job-config-3.xml

<import
        resource="classpath*:/spring/jobs/job-config-1.xml" />
<import
        resource="classpath*:/spring/jobs/job-config-2.xml" />
<batch:job id="jobFlow">

            
    <batch:step id="step1" next="step2">
        <job ref="job1" job-launcher="jobLauncher" />
    </batch:step>

    <batch:step id="step2">
        <job ref="job2" job-launcher="jobLauncher" />
    </batch:step>
    
</batch:job>

What I want to move from XML configuration to Java configuration. I want to create 3 job configuration classes for each of the XMLs. Lets say JobConfig1.java, JobConfig2.java and JobConfig3.java.

JobConfig3.java would import JobConfig1.java and JobConfig2.java; however, I was unable to find an example for this kind of structure in the docs. How do I go about doing this?

Note : I am not looking to use Spring Boot. Just regular Spring Batch through annotation configuration.

2 Answers

The equivalent to your XML configuration would be to define each job in its own configuration class, then import those classes in the main job's configuration. Here is a quick example:

import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobParameters;
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.core.launch.JobLauncher;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
// @Import({Job1Config.class, Job2Config.class}) // use this if the classes where you define sub jobs are not nested
@EnableBatchProcessing
public class MainJobConfig {

    @Bean
    public Job mainJob(JobBuilderFactory jobs, StepBuilderFactory steps, Job job1, Job job2, JobLauncher jobLauncher) {
        return jobs.get("mainJob")
                .start(steps.get("subJob1").job(job1).launcher(jobLauncher).build())
                .next(steps.get("subJob2").job(job2).launcher(jobLauncher).build())
                .build();
    }
    
    @Configuration
    static class Job1Config {
        // define job 1 artefacts (reader, writer, steps, etc)
        // for simplicity, the step is inlined in the job definition
        @Bean
        public Job job1(JobBuilderFactory jobs, StepBuilderFactory steps) {
            return jobs.get("job1")
                    .start(steps.get("job1Step1").tasklet((contribution, chunkContext) -> {
                        System.out.println("job1");
                        return RepeatStatus.FINISHED;
                    }).build())
                    .build();
        }
    }

    @Configuration
    static class Job2Config {
        // define job 2 artefacts (reader, writer, steps, etc)
        // for simplicity, the step is inlined in the job definition
        @Bean
        public Job job2(JobBuilderFactory jobs, StepBuilderFactory steps) {
            return jobs.get("job2")
                    .start(steps.get("job2Step1").tasklet((contribution, chunkContext) -> {
                        System.out.println("job2");
                        return RepeatStatus.FINISHED;
                    }).build())
                    .build();
        }
    }

    public static void main(String[] args) throws Exception {
        ApplicationContext context = new AnnotationConfigApplicationContext(MainJobConfig.class);
        JobLauncher jobLauncher = context.getBean(JobLauncher.class);
        Job job = context.getBean("mainJob", Job.class);
        jobLauncher.run(job, new JobParameters());
    }

}

You can use a JobStep to define your step for example in your Job3 the step1 should look like this :

@Autowired
ApplicationContext context;

public Step step1() {
    return stepBuilderFactory.get("step1").job(context.getBean("job1", Job.class))
            .parametersExtractor(new DefaultJobParametersExtractor()).build();
}

from this, you can run step1 like any other step, but when job3 will appeal this step all steps of job1 will be executed. job1 is a subJob of your principal job3.

for job1 his definition should be like this : (the same as JobConfig2)

@Configuration
class JobConfig1 {

    @Autowired
    private JobBuilderFactory jobBuilderFactory;

    @Autowired
    private StepBuilderFactory stepBuilderFactory;

    @Bean
    public Job job1() {
        return jobBuilderFactory.get("job1").incrementer(new RunIdIncrementer()).start(step1()).build();
    }

    // definition of steps
    public Step step1() {
        return null;
    }
}
Related