skip to content

What is FlowStep, and when would you wrap a Flow as a single Step inside a job?

level: middleimportance: should knowfreq 40%

answer

  1. FlowStep = Flow adapted to Step
  2. StepBuilder.flow(flow).build()
  3. Inner steps run in the SAME JobExecution
  4. No JobParametersExtractor, no JobLauncher
  5. Group a sub-sequence as one branching unit

basics

~20 s

FlowStep 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 s

A `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 lines
java
import 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

for a junior

Knows FlowStep lets a flow be used as a step via StepBuilder.flow(flow).

for a middle

Explains that inner steps run within the same JobExecution and the FlowStep exit status aggregates the flow outcome.

for a senior

Cleanly contrasts FlowStep (same execution) vs JobStep (separate child job) and picks correctly by required isolation/restart semantics.

for a principal

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

context