1. 简介
在使用 Spring Batch 构建批处理任务时,我们通常会将一个 Job 拆分为多个 Step,每个 Step 负责数据的读取、转换或写入。但现实场景往往更复杂——Job 的执行路径并不是一条直线,而是像代码中的 if
语句一样存在分支逻辑。
这种根据条件决定下一步执行哪个 Step 的机制,我们称之为 条件流程(Conditional Flow)。
本文将介绍两种在 Spring Batch 中实现条件流程的常用方式,帮你避开“所有流程都线性执行”的坑,让 Job 更加灵活可控。
2. Exit Status 与 Batch Status 的区别
在设计条件流程前,必须搞清楚两个核心概念:Batch Status 和 Exit Status。它们名字相似,用途却完全不同,混淆它们是新手常踩的坑 ❌。
✅ Batch Status
- 是 Spring Batch 内部用于表示 Step 或 Job 当前运行状态的枚举(
BatchStatus
) - 常见取值包括:
COMPLETED
,FAILED
,STARTED
,STOPPED
,ABANDONED
等 - 它反映的是“执行状态”,比如是否成功完成、是否失败等
✅ Exit Status
- 是 Step 执行结束后对外输出的状态,用于驱动流程跳转
- 类型为
ExitStatus
,可自定义值(如NOTIFY
,SKIP
,LOG_ERROR
) - 默认情况下,
ExitStatus
会继承BatchStatus
,但我们可以通过代码手动覆盖
⚠️ 关键点:**条件流程是基于
ExitStatus
判断的,而不是BatchStatus
**。这是很多开发者一开始搞错的地方。
3. 条件流程实现方式
假设我们有一个 IoT 设备定期上传整数数组形式的测量数据。需求是:只要数据中包含任意正数,就要触发通知。
这就需要我们在批处理 Job 中引入条件判断。下面介绍两种实现思路。
3.1 使用 ExitStatus 控制流程
这是最常见也最直接的方式:通过设置 Step 的 ExitStatus
来决定后续流程走向。
核心机制
- 在
ItemProcessor
、ItemReader
或ItemWriter
中获取StepExecution
- 调用
stepExecution.setExitStatus(new ExitStatus("自定义状态"))
- 在 Job 配置中通过
.on("状态")
匹配并跳转
示例:检测正数并设置 ExitStatus
public class NumberInfoClassifier extends ItemListenerSupport<NumberInfo, Integer>
implements ItemProcessor<NumberInfo, Integer> {
private StepExecution stepExecution;
@BeforeStep
public void beforeStep(StepExecution stepExecution) {
this.stepExecution = stepExecution;
// 初始化为 QUIET,表示无需通知
this.stepExecution.setExitStatus(new ExitStatus("QUIET"));
}
@Override
public Integer process(NumberInfo numberInfo) throws Exception {
return Integer.valueOf(numberInfo.getNumber());
}
@Override
public void afterProcess(NumberInfo item, Integer result) {
super.afterProcess(item, result);
// 如果发现正数,修改 ExitStatus 为 NOTIFY
if (item.isPositive()) {
stepExecution.setExitStatus(new ExitStatus("NOTIFY"));
}
}
}
✅ 提示:虽然这里用了
ItemProcessor
,但你也可以在ItemReader
或ItemWriter
中做类似操作,取决于你的业务逻辑放在哪一层。
配置 Job 流程
new JobBuilder("Number generator - second dataset", jobRepository)
.start(dataProviderStep)
.on("NOTIFY").to(notificationStep) // 如果 ExitStatus 是 NOTIFY,跳转到通知步骤
.end()
.build();
多分支场景
当存在多个条件分支时,可以用 .from()
链式配置:
new JobBuilder("Number generator - second dataset", jobRepository)
.start(dataProviderStep)
.on("NOTIFY").to(notificationStep)
.from(dataProviderStep).on("LOG_ERROR").to(errorLoggingStep)
.end()
.build();
执行效果对比
有正数时:
Second Dataset Processor 11 Second Dataset Processor -2 Second Dataset Processor -3 [Number generator - second dataset] contains interesting data!!
→ 触发了
notificationStep
无正数时:
Second Dataset Processor -1 Second Dataset Processor -2 Second Dataset Processor -3
→ 没有输出通知,流程正常结束
3.2 使用 JobExecutionDecider 实现程序化分支
上面的方法适合基于数据内容做判断。但如果决策逻辑依赖外部系统(如数据库、配置中心、API 调用结果),那就更适合使用 JobExecutionDecider
。
适用场景
- 分支逻辑与 Step 内部数据无关
- 需要调用外部服务或检查全局状态
- 想把流程控制逻辑独立出来,提升可测试性
示例:自定义 Decider
先简化 ItemProcessor
,不再处理状态:
public class NumberInfoClassifierWithDecider implements ItemProcessor<NumberInfo, Integer> {
@Override
public Integer process(NumberInfo numberInfo) throws Exception {
return Integer.valueOf(numberInfo.getNumber());
}
}
然后创建一个 JobExecutionDecider
实现类:
public class NumberInfoDecider implements JobExecutionDecider {
private boolean shouldNotify() {
// 这里可以接入配置中心、数据库查询等外部逻辑
return true; // 简化示例,实际中可能是动态判断
}
@Override
public FlowExecutionStatus decide(JobExecution jobExecution, StepExecution stepExecution) {
if (shouldNotify()) {
return new FlowExecutionStatus("NOTIFY");
} else {
return new FlowExecutionStatus("QUIET");
}
}
}
配置 Job 使用 Decider
new JobBuilder("Number generator - third dataset", jobRepository)
.start(dataProviderStep)
.next(new NumberInfoDecider()) // 执行完 dataProviderStep 后进入 Decider
.on("NOTIFY").to(notificationStep) // 根据返回状态跳转
.end()
.build();
✅ 优势:逻辑解耦,
Decider
可以轻松 mock 测试,适合复杂判断场景。
4. 总结
方式 | 适用场景 | 特点 |
---|---|---|
✅ ExitStatus |
基于 Step 内部数据做判断 | 简单粗暴,适合数据驱动的流程控制 |
✅ JobExecutionDecider |
外部条件或复杂逻辑判断 | 解耦清晰,易于测试,适合配置化分支 |
选择哪种方式,关键看你的判断依据来自哪里:
- 数据在流中 → 用
ExitStatus
- 判断依赖外部系统 → 用
JobExecutionDecider
💡 小技巧:两者也可以结合使用。比如先用
ItemProcessor
设置 ExitStatus,再用 Decider 做二次校验。
附录:完整源码
本文所有示例代码均已开源,可在 GitHub 获取:
👉 https://github.com/baeldung/spring-batch/tree/main/spring-batch-conditional
邮箱联系:support@baeldung.com