Spring Batch: Introduction, Architecture, Core Interfaces, and Practical Implementation Guide

This article provides a comprehensive overview of Spring Batch, covering its purpose, typical business scenarios, core components such as JobRepository, JobLauncher, Job, Step, and core interfaces like ItemReader, ItemProcessor, ItemWriter, followed by detailed code examples for configuration, multi‑step, parallel, decision, nested jobs, data reading, writing, processing, and scheduling using Spring Boot.

Architect's Guide
Architect's Guide
Architect's Guide
Spring Batch: Introduction, Architecture, Core Interfaces, and Practical Implementation Guide

Spring Batch is a lightweight, comprehensive batch‑processing framework built on Spring, designed for robust enterprise batch jobs but not a scheduler itself; it integrates with external schedulers like Quartz.

Business scenarios supported by Spring Batch include:

Periodic batch submission

Concurrent batch processing

Stage‑wise message‑driven processing

Massively parallel batch jobs

Manual or scheduled restarts after failures

Ordered step execution (workflow‑driven)

Skipping records (e.g., on rollback)

Full‑batch transactions for small batches or existing stored procedures

Core architecture components (from the official documentation):

Name

Purpose JobRepository Provides persistence for Job, JobInstance, and Step metadata. JobLauncher Interface used to launch a Job with a set of JobParameters. Job Encapsulates the entire batch process. Step Represents an independent phase of a job.

Core interfaces: ItemReader: reads a chunk of items for a Step. ItemProcessor: processes each item. ItemWriter: writes a chunk of processed items.

The typical flow is ItemReader → ItemProcessor → ItemWriter, where a Job contains multiple Step s.

1. Adding Spring Batch to a Spring Boot project (pom.xml):

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.2.5.RELEASE</version>
</parent>

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-batch</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
  </dependency>
  <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
  </dependency>
</dependencies>

Enable batch processing in the main class:

@SpringBootApplication
@EnableBatchProcessing
public class SpringBatchStartApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringBatchStartApplication.class, args);
    }
}

2. Defining a simple job (FirstJobDemo):

@Component
public class FirstJobDemo {
    @Autowired private JobBuilderFactory jobBuilderFactory;
    @Autowired private StepBuilderFactory stepBuilderFactory;

    @Bean
    public Job firstJob() {
        return jobBuilderFactory.get("firstJob")
                .start(step())
                .build();
    }

    private Step step() {
        return stepBuilderFactory.get("step")
                .tasklet((contribution, chunkContext) -> {
                    System.out.println("执行步骤....");
                    return RepeatStatus.FINISHED;
                }).build();
    }
}

3. Multi‑step job with flow control:

@Bean
public Job multiStepJob() {
    return jobBuilderFactory.get("multiStepJob2")
            .start(step1())
            .on(ExitStatus.COMPLETED.getExitCode()).to(step2())
            .from(step2()).on(ExitStatus.COMPLETED.getExitCode()).to(step3())
            .from(step3()).end()
            .build();
}

private Step step1() { /* tasklet prints "执行步骤一操作。。。" */ }
private Step step2() { /* tasklet prints "执行步骤二操作。。。" */ }
private Step step3() { /* tasklet prints "执行步骤三操作。。。" */ }

4. Parallel execution using split:

@Bean
public Job splitJob() {
    return jobBuilderFactory.get("splitJob")
            .start(flow1())
            .split(new SimpleAsyncTaskExecutor()).add(flow2())
            .end()
            .build();
}

private Flow flow1() { return new FlowBuilder<Flow>("flow1").start(step1()).next(step2()).build(); }
private Flow flow2() { return new FlowBuilder<Flow>("flow2").start(step3()).build(); }

5. Decision making with a JobExecutionDecider:

public class MyDecider implements JobExecutionDecider {
    @Override
    public FlowExecutionStatus decide(JobExecution jobExecution, StepExecution stepExecution) {
        DayOfWeek day = LocalDate.now().getDayOfWeek();
        return (day == DayOfWeek.SATURDAY || day == DayOfWeek.SUNDAY)
                ? new FlowExecutionStatus("weekend")
                : new FlowExecutionStatus("workingDay");
    }
}

Job configuration using the decider routes to different steps based on the returned status.

6. Nested jobs (parent‑child relationship):

@Bean
public Job parentJob() {
    return jobBuilderFactory.get("parentJob")
            .start(childJobOneStep())
            .next(childJobTwoStep())
            .build();
}

private Step childJobOneStep() {
    return new JobStepBuilder(new StepBuilder("childJobOneStep"))
            .job(childJobOne())
            .launcher(jobLauncher)
            .repository(jobRepository)
            .transactionManager(platformTransactionManager)
            .build();
}

private Job childJobOne() { /* simple step that prints a message */ }

7. Reading data (FlatFileItemReader example):

@Component
public class FileItemReaderDemo {
    @Autowired private JobBuilderFactory jobBuilderFactory;
    @Autowired private StepBuilderFactory stepBuilderFactory;

    @Bean
    public Job fileItemReaderJob() {
        return jobBuilderFactory.get("fileItemReaderJob2")
                .start(step())
                .build();
    }

    private Step step() {
        return stepBuilderFactory.get("step")
                .<TestData, TestData>chunk(2)
                .reader(fileItemReader())
                .writer(list -> list.forEach(System.out::println))
                .build();
    }

    private ItemReader<TestData> fileItemReader() {
        FlatFileItemReader<TestData> reader = new FlatFileItemReader<>();
        reader.setResource(new ClassPathResource("reader/file"));
        reader.setLinesToSkip(1);
        DelimitedLineTokenizer tokenizer = new DelimitedLineTokenizer();
        tokenizer.setNames("id", "field1", "field2", "field3");
        DefaultLineMapper<TestData> mapper = new DefaultLineMapper<>();
        mapper.setLineTokenizer(tokenizer);
        mapper.setFieldSetMapper(fieldSet -> {
            TestData data = new TestData();
            data.setId(fieldSet.readInt("id"));
            data.setField1(fieldSet.readString("field1"));
            data.setField2(fieldSet.readString("field2"));
            data.setField3(fieldSet.readString("field3"));
            return data;
        });
        reader.setLineMapper(mapper);
        return reader;
    }
}

8. Writing data to a JSON file (FlatFileItemWriter example):

@Component
public class FileItemWriterDemo {
    @Autowired private JobBuilderFactory jobBuilderFactory;
    @Autowired private StepBuilderFactory stepBuilderFactory;
    @Resource(name = "writerSimpleReader") private ListItemReader<TestData> writerSimpleReader;

    @Bean
    public Job fileItemWriterJob() throws Exception {
        return jobBuilderFactory.get("fileItemWriterJob")
                .start(step())
                .build();
    }

    private Step step() throws Exception {
        return stepBuilderFactory.get("step")
                .<TestData, TestData>chunk(2)
                .reader(writerSimpleReader)
                .writer(fileItemWriter())
                .build();
    }

    private FlatFileItemWriter<TestData> fileItemWriter() throws Exception {
        FlatFileItemWriter<TestData> writer = new FlatFileItemWriter<>();
        FileSystemResource file = new FileSystemResource("D:/code/spring-batch-demo/src/main/resources/writer/writer-file");
        Path path = Paths.get(file.getPath());
        if (!Files.exists(path)) { Files.createFile(path); }
        writer.setResource(file);
        writer.setLineAggregator(item -> {
            try { return new ObjectMapper().writeValueAsString(item); }
            catch (JsonProcessingException e) { e.printStackTrace(); }
            return "";
        });
        writer.afterPropertiesSet();
        return writer;
    }
}

9. Item processing with validation (BeanValidatingItemProcessor):

@Component
public class ValidatingItemProcessorDemo {
    @Autowired private JobBuilderFactory jobBuilderFactory;
    @Autowired private StepBuilderFactory stepBuilderFactory;
    @Resource(name = "processorSimpleReader") private ListItemReader<TestData> processorSimpleReader;

    @Bean
    public Job validatingItemProcessorJob() throws Exception {
        return jobBuilderFactory.get("validatingItemProcessorJob3")
                .start(step())
                .build();
    }

    private Step step() throws Exception {
        return stepBuilderFactory.get("step")
                .<TestData, TestData>chunk(2)
                .reader(processorSimpleReader)
                .processor(beanValidatingItemProcessor())
                .writer(list -> list.forEach(System.out::println))
                .build();
    }

    private BeanValidatingItemProcessor<TestData> beanValidatingItemProcessor() throws Exception {
        BeanValidatingItemProcessor<TestData> processor = new BeanValidatingItemProcessor<>();
        // processor.setFilter(true); // enable filtering of invalid items
        processor.afterPropertiesSet();
        return processor;
    }
}

10. Scheduling the job via a REST controller (can be combined with Quartz or XXL‑Job):

@RestController
@RequestMapping("job")
public class JobController {
    @Autowired private Job job;
    @Autowired private JobLauncher jobLauncher;

    @GetMapping("launcher/{message}")
    public String launcher(@PathVariable String message) throws Exception {
        JobParameters params = new JobParametersBuilder()
                .addString("message", message)
                .toJobParameters();
        jobLauncher.run(job, params);
        return "success";
    }
}

The article concludes with author information and promotional links, which are not part of the technical content.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

backend-developmentJob SchedulingSpring Batchspring-bootbatch-processing
Architect's Guide
Written by

Architect's Guide

Dedicated to sharing programmer-architect skills—Java backend, system, microservice, and distributed architectures—to help you become a senior architect.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.