What is FlowStep, and when would you wrap a Flow as a single Step inside a job?
answer
- FlowStep = Flow adapted to Step
- StepBuilder.flow(flow).build()
- Inner steps run in the SAME JobExecution
- No JobParametersExtractor, no JobLauncher
- Group a sub-sequence as one branching unit
basics
~20 sFlowStep turns a whole Flow into one Step. You build it with StepBuilder.flow(flow). The flow's inner steps run as normal within the same job execution, but the outer job sees them grouped as a single named step.
solid answer
~40 sA `FlowStep` (class `org.springframework.batch.core.job.flow.FlowStep`) adapts a `Flow` so it can be used anywhere a `Step` is expected. You create it via `new StepBuilder("name", jobRepository).flow(flow).build()`. When the FlowStep runs, it executes the flow's inner steps as ordinary steps of the **current** `JobExecution` — they share the same job instance and appear in the step-execution metadata — but the surrounding job treats the whole flow as one logical step with its own `StepExecution` whose status aggregates the flow outcome. Use it to reuse a coherent sub-sequence (validate → normalize → load) as a unit inside multiple jobs, to attach a single set of transitions/listeners to that unit, or to compose flows within splits. Unlike `JobStep`, it does not launch a separate Job, so there is no separate execution identity, no `JobParametersExtractor`, and no separate restart boundary.
code
java · 19 linesimport org.springframework.batch.core.Step;
import org.springframework.batch.core.job.flow.Flow;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.context.annotation.Bean;
public class FlowStepConfig {
@Bean
public Step preProcessingStep(JobRepository jobRepository, Flow preProcessingFlow) {
// Adapt the whole flow into a single Step usable in any job
return new StepBuilder("preProcessingStep", jobRepository)
.flow(preProcessingFlow)
.build();
}
// Now usable anywhere a Step is expected, e.g.:
// new JobBuilder("job", repo).start(preProcessingStep).next(reportStep).build();
}go deeper
Knows FlowStep lets a flow be used as a step via StepBuilder.flow(flow).
Explains that inner steps run within the same JobExecution and the FlowStep exit status aggregates the flow outcome.
Cleanly contrasts FlowStep (same execution) vs JobStep (separate child job) and picks correctly by required isolation/restart semantics.
Reasons about metadata/naming collisions when reusing a flow twice and about the lack of an isolation boundary versus a nested Job.
## The adapter problem A `Flow` is not a `Step`, so you cannot directly hand a flow to APIs that expect a step (for example nesting it in another flow, or referencing it as a step in a job). **`FlowStep`** is the adapter that makes a `Flow` implement the `Step` contract. ## Building it In Spring Batch 5, `StepBuilder` exposes a **`.flow(Flow)`** method that returns a builder producing a `FlowStep`: ```java @Bean Step preProcessingStep(JobRepository jobRepository, Flow preProcessingFlow) { return new StepBuilder("preProcessingStep", jobRepository) .flow(preProcessingFlow) .build(); } ``` You can then use `preProcessingStep` anywhere a `Step` is valid: `JobBuilder.start(preProcessingStep).next(...)`, inside another `FlowBuilder`, or inside a parallel split. ## Execution semantics This is the crux and a frequent interview point: - The FlowStep runs **within the current `JobExecution`** — it does **not** create a new `JobInstance` or `JobExecution`. - The flow's **inner steps execute as normal steps** and each gets its own `StepExecution` persisted under the same job execution (visible in `BATCH_STEP_EXECUTION`). - The FlowStep itself also has a `StepExecution`; its `ExitStatus`/`BatchStatus` is derived from the flow's terminal outcome, so the outer job can branch on the flow result via `.on(...).to(...)`. - Because everything shares one job execution, **restart** works at the level of the enclosing job: on restart, already-completed inner steps are skipped per normal step-restart rules. ## When to use it - **Reuse a sub-sequence as a unit**: e.g. a `preProcessingFlow` embedded in several jobs. - **Group for transitions/branching**: treat the whole flow's success/failure as one exit status the outer job reacts to. - **Compose inside splits/other flows**: since a split works on `Flow` objects and FlowStep bridges Step↔Flow, you can nest structures cleanly. ## FlowStep vs JobStep (don't confuse them) | | FlowStep | JobStep | |---|---|---| | Wraps a | Flow | Job | | New JobExecution? | No — same job execution | Yes — child job runs separately | | JobParametersExtractor | Not used | Used to derive child parameters | | Restart boundary | Enclosing job | Child job has its own instance identity | | Needs JobLauncher | No | Yes | ## Gotchas - Inner step names must be **unique and stable** within the job; duplicate step names across a reused flow embedded twice in the same job cause metadata collisions. - FlowStep gives no isolation: a failure inside the flow marks the FlowStep failed and, by default, fails the enclosing job — there is no separate transaction or execution boundary as with a nested Job. - Don't reach for FlowStep when you actually need a **separately parameterized, independently restartable** unit — that is `JobStep`.
- When a FlowStep runs, where do the flow's inner steps' StepExecutions get recorded?Under the current JobExecution. FlowStep does not start a new job; the inner steps are ordinary steps of the enclosing job, so they appear in the same job execution's step metadata.
- If you need the wrapped unit to be independently parameterized and restartable, is FlowStep the right choice?No — that is JobStep. FlowStep shares the enclosing job's execution and identity; JobStep launches a separate child Job with its own JobInstance and parameters.
saying these in an interview costs you the question
- Claiming FlowStep launches a separate job execution
- Thinking FlowStep needs a JobParametersExtractor or JobLauncher
- Believing FlowStep gives a separate transaction/restart boundary