What is a reusable Flow in Spring Batch, and how do you build one and plug it into a Job?
answer
- FlowBuilder<Flow>("name").start().next().build()
- JobBuilder.start(flow) -> JobFlowBuilder -> .end()
- Flow = reusable step-ordering graph
- No JobInstance/JobExecution of its own
- .on(exitCode).to() for branching
basics
~10 sA Flow is a named, reusable sequence of steps with transitions. You build one with FlowBuilder<Flow>().start(stepA).next(stepB).build(), then start a Job from it with JobBuilder.start(flow).end().build().
solid answer
~40 sA Flow is a first-class Spring Batch object representing an ordered graph of steps and transitions, defined once and reused across jobs. You build it with a FlowBuilder<Flow>("name"), chaining .start(step).next(step) (or .on(exitCode).to(...) for conditional branching), and call .build() to get a Flow bean. To run it, a JobBuilder exposes .start(flow) which returns a JobFlowBuilder; you finish with .end() then .build(). The benefit is modularity: the same Flow can be embedded in multiple jobs, wrapped as a step via FlowStep, or combined into larger flows. Without Flow, step ordering is inlined into each job definition and cannot be shared. In Spring Batch 5 the builders take a JobRepository. Flow itself carries no execution identity; the enclosing Job owns the JobInstance and JobExecution.
code
java · 28 linesimport org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.job.builder.FlowBuilder;
import org.springframework.batch.core.job.flow.Flow;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FlowConfig {
@Bean
public Flow preProcessingFlow(Step validateStep, Step normalizeStep) {
return new FlowBuilder<Flow>("preProcessingFlow")
.start(validateStep)
.next(normalizeStep)
.build();
}
@Bean
public Job reportingJob(JobRepository jobRepository, Flow preProcessingFlow) {
return new JobBuilder("reportingJob", jobRepository)
.start(preProcessingFlow) // JobFlowBuilder
.end() // close the flow
.build();
}
}go deeper
Know that a Flow is a reusable sequence of steps built with FlowBuilder and started via JobBuilder.start(flow).end().
Should explain branching with .on().to(), the JobFlowBuilder/.end() requirement, and that a Flow carries no execution identity.
Contrasts Flow reuse with FlowStep and JobStep, and knows Spring Batch 5 builder wiring (explicit JobRepository, no BuilderFactory).
Frames Flow as the composition primitive underpinning modular batch design and reasons about naming stability for restart metadata.
## What a Flow is In Spring Batch, a **`Job`** is composed of **`Step`** objects executed in an order defined by **transitions** ("after step A, if it completes, go to step B"). A **`Flow`** (interface `org.springframework.batch.core.job.flow.Flow`) is that ordering graph extracted into its own reusable, named object. Think of a Flow as "a chunk of the job's step-wiring" that you can define once and reuse. ### Why it exists Without Flow, the step order lives inline inside a single `JobBuilder` chain, so you cannot share a sequence of steps across jobs or treat a sub-sequence as a unit. Flow gives you **modular composition**: define `preProcessingFlow` once, then embed it in several jobs, wrap it as a step (`FlowStep`), or nest it inside a bigger flow. ## Building a Flow Use **`FlowBuilder<Flow>`** (in `org.springframework.batch.core.job.builder`). The generic type is the build product; `FlowBuilder<Flow>` yields a plain `Flow`. ```java Flow flow = new FlowBuilder<Flow>("preProcessingFlow") .start(stepA) .next(stepB) .build(); ``` Key builder methods: - **`.start(step)`** — the entry step. - **`.next(step)`** — unconditional successor. - **`.on("EXIT_CODE").to(step)`** — conditional transition based on a step's `ExitStatus` (supports `*`/`?` wildcards). - **`.from(step).on(...).to(...)`** — branch from an earlier node. - **`.end()`** — terminates a branch (in FlowBuilder) or, on a `JobFlowBuilder`, closes the flow so the job can build. ## Plugging a Flow into a Job `JobBuilder` has an overload **`.start(Flow)`** that returns a `JobFlowBuilder`. Because a flow can have open transitions, you must close it with **`.end()`** before `.build()`: ```java @Bean Job job(JobRepository jobRepository, Flow preProcessingFlow) { return new JobBuilder("reportingJob", jobRepository) .start(preProcessingFlow) // returns JobFlowBuilder .end() // close the flow .build(); } ``` Contrast: `.start(Step)` returns a `SimpleJobBuilder` and does **not** need `.end()`. ## Execution identity A Flow has **no execution identity of its own**. It does not create a `JobInstance` or `JobExecution`; the enclosing Job owns those. The flow's steps produce ordinary `StepExecution`s that belong to the enclosing job's `JobExecution`. This distinguishes plain Flow reuse (and `FlowStep`) from `JobStep`, which launches a *separate* child Job with its own execution identity. ## Gotchas - **`.end()` required** when a Job starts from a Flow — forgetting it is a common compile/build confusion. - A `Flow` bean is a **definition template**, not per-run state; it is safe to reuse across jobs but the steps inside still record executions under whatever job is running them. - Naming matters: flow and step names appear in the `BATCH_STEP_EXECUTION`/metadata and must be stable for restart semantics. - In Spring Batch 5, `throttleLimit`/allocation and repository wiring moved into the builders — the older `JobBuilderFactory`/`StepBuilderFactory` are removed; construct builders with an explicit `JobRepository`.
- Why does JobBuilder.start(Flow) require a trailing .end() while start(Step) does not?start(Step) returns a SimpleJobBuilder for a straight-line job. start(Flow) returns a JobFlowBuilder because a flow may contain open/branching transitions; .end() closes the flow into a finished FlowJob before .build().
- Does reusing the same Flow bean in two jobs share execution state?No. The Flow is only a definition. Each job run gets its own JobExecution, and the flow's steps record separate StepExecutions under whichever job is executing them.
saying these in an interview costs you the question
- Thinking a Flow creates its own JobExecution or JobInstance
- Believing .start(Flow) builds without .end()
- Confusing Flow (definition) with a running instance/state