Master Spring Batch: From Basics to Advanced Job Configurations
This article provides a comprehensive guide to Spring Batch, covering its purpose, supported business scenarios, core components and interfaces, Maven setup, sample job definitions, parallel execution, decision flows, nested jobs, data reading and writing, validation processing, and REST‑based job scheduling, all illustrated with complete code examples.
1. Spring Batch Introduction
Spring Batch is a lightweight, comprehensive batch‑processing framework designed to support robust batch applications essential for daily enterprise operations. It builds on Spring Framework features such as productivity, POJO‑based development, and ease of use, while allowing access to advanced enterprise services when needed.
Spring Batch is not a scheduling framework; it is intended to be used together with external schedulers like Quartz, Tivoli, or Control‑M.
2. Business Scenarios
Spring Batch supports the following scenarios:
Periodic batch submission.
Concurrent batch processing: parallel job execution.
Stage‑wise, message‑driven enterprise processing.
Large‑scale parallel batch processing.
Manual or scheduled restart after failure.
Ordered processing of related steps (extensible to workflow‑driven batches).
Partial processing: skipping records (e.g., during rollback).
Whole‑batch transactions suitable for small batches or when stored procedures/scripts already exist.
3. Fundamentals
3.1 Overall Architecture
Key components (from the official documentation):
JobRepository – provides persistence for all job‑related entities (Job, JobInstance, Step).
JobLauncher – a simple interface used to launch a Job with a given set of JobParameters.
Job – encapsulates the entire batch process.
Step – represents an independent sequential phase of a job.
3.2 Core Interfaces
ItemReader: abstraction that reads a chunk of items for a Step. ItemProcessor: abstraction that performs business processing on an item. ItemWriter: abstraction that writes a chunk of items for a Step.
The typical flow is Input → Data Processing → Output, where a Job contains multiple Step s, each usually composed of an ItemReader, ItemProcessor, and ItemWriter.
4. Practical Implementation
4.0 Adding Spring Batch
<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>
</dependencies>MySQL schema can be found inside the Spring Batch JAR at org/springframework/batch/core/schema-mysql.sql.
@SpringBootApplication
@EnableBatchProcessing
public class SpringBatchStartApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBatchStartApplication.class, args);
}
}4.1 Sample Job Definition
@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("Executing step...");
return RepeatStatus.FINISHED;
}).build();
}
}4.2 Flow Control
A. Multi‑step Job
@Bean
public Job multiStepJob() {
return jobBuilderFactory.get("multiStepJob")
.start(step1())
.on(ExitStatus.COMPLETED.getExitCode()).to(step2())
.from(step2()).on(ExitStatus.COMPLETED.getExitCode()).to(step3())
.from(step3()).end()
.build();
}
private Step step1() { /* tasklet implementation */ }
private Step step2() { /* tasklet implementation */ }
private Step step3() { /* tasklet implementation */ }B. Parallel Execution
@Bean
public Job splitJob() {
return jobBuilderFactory.get("splitJob")
.start(flow1())
.split(new SimpleAsyncTaskExecutor())
.add(flow2())
.end()
.build();
}
private Flow flow1() { return new FlowBuilder<>("flow1").start(step1()).next(step2()).build(); }
private Flow flow2() { return new FlowBuilder<>("flow2").start(step3()).build(); }C. Decision Flow
@Component
public class MyDecider implements JobExecutionDecider {
@Override
public FlowExecutionStatus decide(JobExecution jobExecution, StepExecution stepExecution) {
DayOfWeek day = LocalDate.now().getDayOfWeek();
if (day == DayOfWeek.SATURDAY || day == DayOfWeek.SUNDAY) {
return new FlowExecutionStatus("weekend");
} else {
return new FlowExecutionStatus("workingDay");
}
}
}
@Bean
public Job deciderJob() {
return jobBuilderFactory.get("deciderJob")
.start(step1())
.next(myDecider)
.from(myDecider).on("weekend").to(step2())
.from(myDecider).on("workingDay").to(step3())
.from(step3()).on("*").to(step4())
.end()
.build();
}D. Nested Jobs
@Component
public class NestedJobDemo {
@Autowired private JobBuilderFactory jobBuilderFactory;
@Autowired private StepBuilderFactory stepBuilderFactory;
@Autowired private JobLauncher jobLauncher;
@Autowired private JobRepository jobRepository;
@Autowired private PlatformTransactionManager platformTransactionManager;
@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 Step childJobTwoStep() { /* similar to above */ }
private Job childJobOne() { /* simple step that prints a message */ }
private Job childJobTwo() { /* simple step that prints a message */ }
}4.3 Reading Data
Define a POJO model:
@Data
public class TestData {
private int id;
private String field1;
private String field2;
private String field3;
}FlatFileItemReader configuration (CSV example):
@Bean
public Job fileItemReaderJob() {
return jobBuilderFactory.get("fileItemReaderJob")
.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;
}4.4 Writing Data
@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;
}
}4.5 Processing Data (Validation)
@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("validatingItemProcessorJob")
.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.afterPropertiesSet();
return processor;
}
}4.6 Job Scheduling via REST
@RestController
@RequestMapping("job")
public class JobController {
@Autowired private Job job;
@Autowired private JobLauncher jobLauncher;
@GetMapping("launcher/{message}")
public String launch(@PathVariable String message) throws Exception {
JobParameters parameters = new JobParametersBuilder()
.addString("message", message)
.toJobParameters();
jobLauncher.run(job, parameters);
return "success";
}
}Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Architect's Guide
Dedicated to sharing programmer-architect skills—Java backend, system, microservice, and distributed architectures—to help you become a senior architect.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
