作业流与条件决策
本章定位:彻底掌握 Spring Batch Job 内部的 Step 编排——顺序执行、条件分支、并发流和 Flow 复用的全部模式。
定义与作用
Spring Batch 提供了 5 种 Step 编排模式,通过 JobBuilder 的流式 API 构建。
完整示例
场景一:飞翔科技——ETL 顺序流
@Bean
public Job etlJob(JobRepository jobRepository,
Step extractStep, Step transformStep, Step loadStep) {
return new JobBuilder("etlJob", jobRepository)
.start(extractStep) // E
.next(transformStep) // T
.next(loadStep) // L
.build();
}
场景二:条件分支——根据 Step 退出码路由
@Bean
public Job conditionalJob(JobRepository jobRepository,
Step validationStep,
Step processStep,
Step notifyStep,
Step failStep) {
return new JobBuilder("conditionalJob", jobRepository)
.start(validationStep)
.on("FAILED").to(failStep)
.on("DATA_MISSING").to(notifyStep)
.from(validationStep)
.on("*").to(processStep) // 其他情况
.end()
.build();
}
场景三:并发流 + Flow 复用
@Bean
public Job parallelJob(JobRepository jobRepository,
Step initStep, Step finalStep,
Step dbStep1, Step dbStep2,
Step fileStep1, Step fileStep2) {
Flow dbFlow = new FlowBuilder<Flow>("dbFlow")
.start(dbStep1)
.next(dbStep2)
.build();
Flow fileFlow = new FlowBuilder<Flow>("fileFlow")
.start(fileStep1)
.next(fileStep2)
.build();
return new JobBuilder("parallelJob", jobRepository)
.start(initStep)
.split(new SimpleAsyncTaskExecutor())
.add(dbFlow, fileFlow)
.next(finalStep)
.end()
.build();
}
运行结果:
Job: parallelJob
initStep → COMPLETED
[并发] dbFlow: dbStep1 → dbStep2 → COMPLETED
[并发] fileFlow: fileStep1 → fileStep2 → COMPLETED
finalStep → COMPLETED
场景四:程序化决策——JobExecutionDecider
@Component
public class SkipThresholdDecider implements JobExecutionDecider {
@Override
public FlowExecutionStatus decide(JobExecution jobExecution,
StepExecution stepExecution) {
long skipCount = stepExecution.getSkipCount();
return skipCount > 100
? new FlowExecutionStatus("TOO_MANY_SKIPS")
: FlowExecutionStatus.COMPLETED;
}
}
易错场景与避坑
反例一:忘记 end() → Step 顺序不可预测
// ❌ 缺少 end() → Job 结束状态不确定
return new JobBuilder("job", jobRepository)
.start(stepA).on("COMPLETED").to(stepB)
.from(stepA).on("FAILED").to(stepC)
// ❌ 忘记 .end() → Job 可能 FAILED 但 Step 都成功了
.build();
反例二:split 但不使用异步 TaskExecutor
// ❌ 使用 SyncTaskExecutor → Step B 和 C 串行执行
.split(new SyncTaskExecutor())
.add(flowB, flowC)
面试高频考点
Q1:on("*") 的含义?
匹配任何 ExitStatus。但
*不匹配FAILED(Job 失败时不会进入后续 Step)。要用on("FAILED")显式处理失败分支。
Q2:Flow 和 Step 的区别?
Flow 是一组 Step 的编排容器,可包含 Step、其他 Flow、Split 和 Decision。Flow 可以独立复用(被多个 Job 引用),但本身不创建事务边界——事务由每个 Step 独立管理。